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.9 2005/08/12 08:59:17 taylor
21 * Revision 1.8 2004/09/20 01:31:44 theoddone33
24 * Revision 1.7 2004/06/11 01:49:45 tigital
25 * byte-swapping changes for bigendian systems
27 * Revision 1.6 2003/08/03 16:10:29 taylor
28 * cleanup; compile warning fixes
30 * Revision 1.5 2002/06/17 06:33:10 relnev
31 * ryan's struct patch for gcc 2.95
33 * Revision 1.4 2002/06/09 04:41:24 relnev
34 * added copyright header
36 * Revision 1.3 2002/05/26 20:49:54 theoddone33
39 * Revision 1.2 2002/05/07 03:16:47 theoddone33
40 * The Great Newline Fix
42 * Revision 1.1.1.1 2002/05/03 03:28:10 root
46 * 83 9/14/99 2:21p Dave
47 * Fixed observer mode joining and ingame stuff.
49 * 82 9/14/99 3:26a Dave
50 * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
51 * respawn-too-early problem. Made a few crash points safe.
53 * 81 9/13/99 4:52p Dave
56 * 80 9/08/99 10:01p Dave
57 * Make sure game won't run in a drive's root directory. Make sure
58 * standalone routes suqad war messages properly to the host.
60 * 79 8/28/99 4:54p Dave
61 * Fixed directives display for multiplayer clients for wings with
62 * multiple waves. Fixed hud threat indicator rendering color.
64 * 78 8/27/99 12:32a Dave
65 * Allow the user to specify a local port through the launcher.
67 * 77 8/26/99 8:51p Dave
68 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
70 * 76 8/25/99 4:38p Dave
71 * Updated PXO stuff. Make squad war report stuff much more nicely.
73 * 75 8/24/99 1:50a Dave
74 * Fixed client-side afterburner stuttering. Added checkbox for no version
75 * checking on PXO join. Made button info passing more friendly between
78 * 74 8/22/99 5:53p Dave
79 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
80 * instead of ship designations for multiplayer players.
82 * 73 8/22/99 1:55p Dave
83 * Cleaned up host/team-captain leaving code.
85 * 72 8/22/99 1:19p Dave
86 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
87 * which d3d cards are detected.
89 * 71 8/19/99 10:59a Dave
90 * Packet loss detection.
92 * 70 8/17/99 1:12p Dave
93 * Send TvT update when a client has finished joining so he stays nice and
96 * 69 8/16/99 4:05p Dave
97 * Big honking checkin.
99 * 68 8/11/99 5:54p Dave
100 * Fixed collision problem. Fixed standalone ghost problem.
102 * 67 8/06/99 9:46p Dave
103 * Hopefully final changes for the demo.
105 * 66 8/05/99 2:06a Dave
108 * 65 7/30/99 7:01p Dave
109 * Dogfight escort gauge. Fixed up laser rendering in Glide.
111 * 64 7/29/99 5:41p Jefff
112 * Sound hooks for cmeasure success
114 * 63 7/28/99 5:34p Dave
115 * Nailed the missing stats bug to the wall. Problem was optimized build
116 * and using GET_DATA() with array elements. BLECH.
118 * 62 7/26/99 5:50p Dave
119 * Revised ingame join. Better? We'll see....
121 * 61 7/24/99 1:54p Dave
122 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
125 * 60 7/22/99 7:17p Dave
126 * Fixed excessive whacks in multiplayer.
128 * 59 7/08/99 10:53a Dave
129 * New multiplayer interpolation scheme. Not 100% done yet, but still
130 * better than the old way.
132 * 58 7/03/99 5:50p Dave
133 * Make rotated bitmaps draw properly in padlock views.
135 * 57 7/03/99 4:08p Dave
136 * Fixed wss_slots size issues. Fixed potentially nasty bug in low level
139 * 56 6/21/99 7:24p Dave
140 * netplayer pain packet. Added type E unmoving beams.
142 * 55 6/18/99 5:16p Dave
143 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
144 * dialog to PXO screen.
146 * 54 6/16/99 4:06p Dave
147 * New pilot info popup. Added new draw-bitmap-as-poly function.
149 * 53 6/16/99 10:20a Dave
150 * Added send-message-list sexpression.
152 * 52 6/04/99 3:52p Anoop
153 * Removed bogus assert.
155 * 51 6/01/99 8:35p Dave
156 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
157 * awacs-set-radius sexpression.
159 * 50 5/21/99 5:03p Andsager
160 * Add code to display engine wash death. Modify ship_kill_packet
162 * 49 5/18/99 1:30p Dave
163 * Added muzzle flash table stuff.
165 * 48 5/14/99 1:59p Andsager
166 * Multiplayer message for subsystem cargo revealed.
168 * 47 5/14/99 12:15p Andsager
169 * Add vaporized to kill packet
171 * 46 5/03/99 8:32p Dave
172 * New version of multi host options screen.
174 * 45 4/30/99 12:18p Dave
175 * Several minor bug fixes.
177 * 44 4/29/99 2:29p Dave
178 * Made flak work much better in multiplayer.
180 * 43 4/28/99 11:13p Dave
181 * Temporary checkin of artillery code.
183 * 42 4/16/99 5:54p Dave
184 * Support for on/off style "stream" weapons. Real early support for
185 * target-painting lasers.
187 * 41 4/12/99 2:22p Dave
188 * More checks for dogfight stats.
190 * 40 4/09/99 2:21p Dave
191 * Multiplayer beta stuff. CD checking.
193 * 39 4/02/99 9:55a Dave
194 * Added a few more options in the weapons.tbl for beam weapons. Attempt
195 * at putting "pain" packets into multiplayer.
197 * 38 4/01/99 3:41p Anoop
198 * Removed bogus Int3().
200 * 37 3/19/99 9:51a Dave
201 * Checkin to repair massive source safe crash. Also added support for
202 * pof-style nebulae, and some new weapons code.
204 * 38 3/12/99 2:32p Anoop
205 * Removed bogus asserts.
207 * 37 3/11/99 11:41a Neilk
208 * Don't do multi_io_* operations in single-player
210 * 36 3/10/99 6:50p Dave
211 * Changed the way we buffer packets for all clients. Optimized turret
212 * fired packets. Did some weapon firing optimizations.
214 * 35 3/09/99 6:24p Dave
215 * More work on object update revamping. Identified several sources of
216 * unnecessary bandwidth.
218 * 34 3/08/99 7:03p Dave
219 * First run of new object update system. Looks very promising.
221 * 33 3/04/99 6:09p Dave
222 * Added in sexpressions for firing beams and checking for if a ship is
225 * 32 3/01/99 10:00a Dave
226 * Fxied several dogfight related stats bugs.
228 * 31 2/24/99 2:25p Dave
229 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
230 * bug for dogfight more.
232 * 30 2/23/99 2:29p Dave
233 * First run of oldschool dogfight mode.
235 * 29 2/21/99 6:01p Dave
236 * Fixed standalone WSS packets.
238 * 28 2/21/99 1:48p Dave
239 * Some code for monitoring datarate for multiplayer in detail.
241 * 27 2/17/99 2:11p Dave
242 * First full run of squad war. All freespace and tracker side stuff
245 * 26 2/12/99 6:16p Dave
246 * Pre-mission Squad War code is 95% done.
248 * 25 2/11/99 3:08p Dave
249 * PXO refresh button. Very preliminary squad war support.
251 * 24 1/29/99 5:07p Dave
252 * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
255 * 23 1/27/99 9:56a Dave
256 * Temporary checkin of beam weapons for Dan to make cool sounds.
258 * 22 1/26/99 6:33p Anoop
259 * Fixed multiplayer slot switching problem (be sure to remember that
260 * hinfo->id is player id# _not_ player index #)
262 * 21 1/24/99 11:37p Dave
263 * First full rev of beam weapons. Very customizable. Removed some bogus
264 * Int3()'s in low level net code.
266 * 20 1/15/99 4:37p Dave
267 * Potential fix for weapon pair problem.
269 * 19 1/14/99 6:06p Dave
270 * 100% full squad logo support for single player and multiplayer.
272 * 18 1/14/99 12:48a Dave
273 * Todo list bug fixes. Made a pass at putting briefing icons back into
274 * FRED. Sort of works :(
276 * 17 1/12/99 5:45p Dave
277 * Moved weapon pipeline in multiplayer to almost exclusively client side.
278 * Very good results. Bandwidth goes down, playability goes up for crappy
279 * connections. Fixed object update problem for ship subsystems.
281 * 16 1/08/99 4:56p Anoop
282 * Fixed a problem with wss request packets.
284 * 15 12/18/98 12:24p Markm
285 * Fixed a dumb bug where player image_filenames were not being passed
286 * properly in new players packet.
288 * 14 12/14/98 12:13p Dave
289 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
292 * 13 11/30/98 1:07p Dave
293 * 16 bit conversion, first run.
295 * 12 11/20/98 4:08p Dave
296 * Fixed flak effect in multiplayer.
298 * 11 11/19/98 4:19p Dave
299 * Put IPX sockets back in psnet. Consolidated all multiplayer config
302 * 10 11/19/98 8:04a Dave
303 * Full support for D3-style reliable sockets. Revamped packet lag/loss
304 * system, made it receiver side and at the lowest possible level.
306 * 9 11/17/98 11:12a Dave
307 * Removed player identification by address. Now assign explicit id #'s.
309 * 8 11/12/98 11:50a Dave
310 * Multiplayer clients set flak range to be very long.
312 * 7 11/12/98 12:13a Dave
313 * Tidied code up for multiplayer test. Put in network support for flak
316 * 6 11/05/98 5:55p Dave
317 * Big pass at reducing #includes
319 * 5 10/20/98 1:39p Andsager
320 * Make so sparks follow animated ship submodels. Modify
321 * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
322 * submodel_num. Add submodel_num to multiplayer hit packet.
324 * 4 10/13/98 9:29a Dave
325 * Started neatening up freespace.h. Many variables renamed and
326 * reorganized. Added AlphaColors.[h,cpp]
328 * 3 10/07/98 6:27p Dave
329 * Globalized mission and campaign file extensions. Removed Silent Threat
330 * special code. Moved \cache \players and \multidata into the \data
333 * 2 10/07/98 10:53a Dave
336 * 1 10/07/98 10:50a Dave
338 * 506 10/02/98 3:22p Allender
339 * fix up the -connect option and fix the -port option
341 * 505 10/02/98 11:45a Dave
342 * Fixed stupid chat message bug.
344 * 504 9/29/98 1:33p Dave
345 * Remove standalone only conditional compiles for pre 1.04 stuff.
347 * 503 9/28/98 1:54p Dave
348 * Make sure French and German don't xfer builtin files they don't have
351 * 502 9/20/98 7:19p Dave
352 * Added CHANGE_IFF packet.
354 * 501 9/17/98 3:08p Dave
355 * PXO to non-pxo game warning popup. Player icon stuff in create and join
356 * game screens. Upped server count refresh time in PXO to 35 secs (from
365 #include <io.h> // for findfirst/findnext, etc
368 #include "multimsgs.h"
369 #include "multiutil.h"
372 #include "multiteamselect.h"
373 #include "linklist.h"
374 #include "gamesequence.h"
375 #include "hudmessage.h"
376 #include "hudsquadmsg.h"
377 #include "freespace.h"
381 #include "missiongoals.h"
382 #include "missionparse.h"
383 #include "missionlog.h"
384 #include "missionmessage.h"
385 #include "missionbrief.h"
387 #include "cmeasure.h"
388 #include "model.h" // for some limits
389 #include "afterburner.h"
390 #include "stand_gui.h"
391 #include "multi_xfer.h"
397 #include "managepilot.h"
398 #include "hudsquadmsg.h"
400 #include "missionweaponchoice.h"
401 #include "missionshipchoice.h"
402 #include "fireballs.h"
405 #include "multi_ingame.h"
406 #include "multiteamselect.h"
408 #include "multi_campaign.h"
409 #include "multi_team.h"
410 #include "multi_respawn.h"
411 #include "multi_observer.h"
412 #include "multi_voice.h"
413 #include "asteroid.h"
414 #include "multi_pmsg.h"
415 #include "multi_data.h"
416 #include "multi_options.h"
417 #include "objcollide.h"
418 #include "hudreticle.h"
419 #include "multi_pause.h"
420 #include "multi_endgame.h"
421 #include "missiondebrief.h"
422 #include "multi_obj.h"
423 #include "multi_log.h"
425 #include "multi_kick.h"
429 #include "multi_rate.h"
430 #include "neblightning.h"
431 #include "hudescort.h"
433 // #define _MULTI_SUPER_WACKY_COMPRESSION
435 #ifdef _MULTI_SUPER_WACKY_COMPRESSION
437 #define MAX_CODE ( ( 1 << BITS ) - 1 )
438 #define TABLE_SIZE 35023L
439 #define END_OF_STREAM 256
440 #define BUMP_CODE 257
441 #define FLUSH_CODE 258
442 #define FIRST_CODE 259
451 static DICTIONARY dict[TABLE_SIZE];
452 static char decode_stack[TABLE_SIZE];
453 static uint next_code;
454 static int current_code_bits;
455 static uint next_bump_code;
457 typedef struct BitBuf {
463 void output_bits( BitBuf *bitbuf, uint code, int count )
467 mask = 1L << ( count - 1 );
470 bitbuf->rack |= bitbuf->mask;
472 if ( bitbuf->mask == 0 ) {
473 *bitbuf->data++=(ubyte)bitbuf->rack;
481 uint input_bits( BitBuf *bitbuf, int bit_count )
486 mask = 1L << ( bit_count - 1 );
489 if ( bitbuf->mask == 0x80 ) {
490 bitbuf->rack = *bitbuf->data++;
491 if ( bitbuf->rack == EOF )
492 return END_OF_STREAM;
494 if ( bitbuf->rack & bitbuf->mask )
495 return_value |= mask;
498 if ( bitbuf->mask == 0 )
501 return( return_value );
505 static void InitializeDictionary()
509 for ( i = 0 ; i < TABLE_SIZE ; i++ )
510 dict[i].code_value = UNUSED;
512 next_code = FIRST_CODE;
513 current_code_bits = 9;
514 next_bump_code = 511;
518 static uint find_child_node( int parent_code, int child_character )
523 index = ( child_character << ( BITS - 8 ) ) ^ parent_code;
527 offset = TABLE_SIZE - index;
529 if ( dict[ index ].code_value == UNUSED )
530 return( (uint) index );
531 if ( dict[ index ].parent_code == parent_code &&
532 dict[ index ].character == (char) child_character )
534 if ( (int) index >= offset )
537 index += TABLE_SIZE - offset;
542 static uint decode_string( uint count, uint code )
544 while ( code > 255 ) {
545 decode_stack[ count++ ] = dict[ code ].character;
546 code = dict[ code ].parent_code;
548 decode_stack[ count++ ] = (char) code;
552 int lzw_compress( ubyte *outputbuf, ubyte *inputbuf, int input_size )
560 // Init output bit buffer
563 output.data = outputbuf;
565 InitializeDictionary();
567 string_code = *inputbuf++;
569 for ( i=1 ; i<input_size ; i++ ) {
570 character = *inputbuf++;
571 index = find_child_node( string_code, character );
572 if ( dict[ index ].code_value != - 1 )
573 string_code = dict[ index ].code_value;
575 dict[ index ].code_value = next_code++;
576 dict[ index ].parent_code = string_code;
577 dict[ index ].character = (char) character;
578 output_bits( &output, (unsigned long) string_code, current_code_bits );
579 string_code = character;
580 if ( next_code > MAX_CODE ) {
581 output_bits( &output, (unsigned long) FLUSH_CODE, current_code_bits );
582 InitializeDictionary();
583 } else if ( next_code > next_bump_code ) {
584 output_bits( &output, (unsigned long) BUMP_CODE, current_code_bits );
586 next_bump_code <<= 1;
591 output_bits( &output, (unsigned long) string_code, current_code_bits );
592 output_bits( &output, (unsigned long) END_OF_STREAM, current_code_bits);
594 if ( output.mask != 0x80 )
595 *output.data++ = (ubyte)output.rack;
597 return output.data-outputbuf;
601 int lzw_expand( ubyte *outputbuf, ubyte *inputbuf )
612 input.data = inputbuf;
616 InitializeDictionary();
617 old_code = (uint) input_bits( &input, current_code_bits );
618 if ( old_code == END_OF_STREAM )
620 character = old_code;
621 outputbuf[counter++] = ( ubyte )old_code;
623 new_code = (uint) input_bits( &input, current_code_bits );
624 if ( new_code == END_OF_STREAM )
626 if ( new_code == FLUSH_CODE )
628 if ( new_code == BUMP_CODE ) {
632 if ( new_code >= next_code ) {
633 decode_stack[ 0 ] = (char) character;
634 count = decode_string( 1, old_code );
636 count = decode_string( 0, new_code );
638 character = decode_stack[ count - 1 ];
640 outputbuf[counter++] = ( ubyte )decode_stack[ --count ];
641 dict[ next_code ].parent_code = old_code;
642 dict[ next_code ].character = (char) character;
650 // process a join request packet add
651 void add_join_request(ubyte *data, int *size, join_request *jr)
653 int packet_size = *size;
654 join_request *jr_tmp = jr;
656 jr_tmp->tracker_id = INTEL_INT(jr->tracker_id);
657 jr_tmp->player_options.flags = INTEL_INT(jr->player_options.flags);
658 jr_tmp->player_options.obj_update_level = INTEL_INT(jr->player_options.obj_update_level);
665 // process a join request packet get
666 void get_join_request(ubyte *data, int *size, join_request jr)
672 jr.tracker_id = INTEL_INT(jr.tracker_id);
673 jr.player_options.flags = INTEL_INT(jr.player_options.flags);
674 jr.player_options.obj_update_level = INTEL_INT(jr.player_options.obj_update_level);
679 void add_net_addr(ubyte *data, int *size, net_addr addr)
681 int packet_size = *size;
682 net_addr addr_tmp = addr;
684 addr_tmp.type = INTEL_INT(addr.type);
685 addr_tmp.port = INTEL_SHORT(addr.port);
692 void get_net_addr(ubyte *data, int *size, net_addr addr)
698 addr.type = INTEL_INT(addr.type);
699 addr.port = INTEL_SHORT(addr.port);
704 void add_vector_data(ubyte *data, int *size, vector vec)
706 int packet_size = *size;
708 ADD_DATA_FL(vec.xyz.x);
709 ADD_DATA_FL(vec.xyz.y);
710 ADD_DATA_FL(vec.xyz.z);
715 void get_vector_data(ubyte *data, int *size, vector vec)
719 GET_DATA_FL(vec.xyz.x);
720 GET_DATA_FL(vec.xyz.y);
721 GET_DATA_FL(vec.xyz.z);
726 // send the specified data packet to all players
727 void multi_io_send(net_player *pl, ubyte *data, int len)
730 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
734 // don't do it for single player
735 if(!(Game_mode & GM_MULTIPLAYER)){
740 if(MULTIPLAYER_CLIENT){
741 // Assert(pl == Net_player);
742 if(pl != Net_player){
746 // Assert(pl != Net_player);
747 if(pl == Net_player){
752 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
753 if ((pl->s_info.unreliable_buffer_size + len) > MAX_PACKET_SIZE) {
754 multi_io_send_force(pl);
755 pl->s_info.unreliable_buffer_size = 0;
758 Assert((pl->s_info.unreliable_buffer_size + len) <= MAX_PACKET_SIZE);
760 memcpy(pl->s_info.unreliable_buffer + pl->s_info.unreliable_buffer_size, data, len);
761 pl->s_info.unreliable_buffer_size += len;
764 void multi_io_send_to_all(ubyte *data, int length, net_player *ignore)
767 Assert(MULTIPLAYER_MASTER);
769 // need to check for i > 1, hmmm... and connected. I don't know.
770 for (i = 0; i < MAX_PLAYERS; i++ ) {
771 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
775 // maybe ignore a player
776 if((ignore != NULL) && (&Net_players[i] == ignore)){
780 // ingame joiners not waiting to select a ship doesn't get any packets
781 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
786 multi_io_send(&Net_players[i], data, length);
790 void multi_io_send_force(net_player *pl)
793 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
797 // don't do it for single player
798 if(!(Game_mode & GM_MULTIPLAYER)){
802 // send everything in
803 if (MULTIPLAYER_MASTER) {
804 psnet_send(&pl->p_info.addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
806 // add the bytes sent to this player
807 pl->sv_bytes_sent += pl->s_info.unreliable_buffer_size;
809 psnet_send(&Netgame.server_addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
811 pl->s_info.unreliable_buffer_size = 0;
814 // send the data packet to all players via their reliable sockets
815 void multi_io_send_reliable(net_player *pl, ubyte *data, int len)
818 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
822 // don't do it for single player
823 if(!(Game_mode & GM_MULTIPLAYER)){
828 if(MULTIPLAYER_CLIENT){
829 // Assert(pl == Net_player);
830 if(pl != Net_player){
834 // Assert(pl != Net_player);
835 if(pl == Net_player){
840 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
841 if ((pl->s_info.reliable_buffer_size + len) > MAX_PACKET_SIZE) {
842 multi_io_send_reliable_force(pl);
843 pl->s_info.reliable_buffer_size = 0;
846 Assert((pl->s_info.reliable_buffer_size + len) <= MAX_PACKET_SIZE);
848 memcpy(pl->s_info.reliable_buffer + pl->s_info.reliable_buffer_size, data, len);
849 pl->s_info.reliable_buffer_size += len;
852 void multi_io_send_to_all_reliable(ubyte* data, int length, net_player *ignore)
855 Assert(MULTIPLAYER_MASTER);
857 // need to check for i > 1, hmmm... and connected. I don't know.
858 for (i = 0; i < MAX_PLAYERS; i++ ) {
859 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
863 // maybe ignore a player
864 if((ignore != NULL) && (&Net_players[i] == ignore)){
868 // ingame joiners not waiting to select a ship doesn't get any packets
869 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
874 multi_io_send_reliable(&Net_players[i], data, length);
878 void multi_io_send_reliable_force(net_player *pl)
881 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
885 // don't do it for single player
886 if(!(Game_mode & GM_MULTIPLAYER)){
890 // send everything in
891 if(MULTIPLAYER_MASTER) {
892 psnet_rel_send(pl->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
893 } else if(Net_player != NULL){
894 psnet_rel_send(Net_player->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
896 pl->s_info.reliable_buffer_size = 0;
899 // send all buffered packets
900 void multi_io_send_buffered_packets()
904 // don't do it for single player
905 if(!(Game_mode & GM_MULTIPLAYER)){
910 if(MULTIPLAYER_MASTER){
911 for(idx=0; idx<MAX_PLAYERS; idx++){
912 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
913 // force unreliable data
914 if(Net_players[idx].s_info.unreliable_buffer_size > 0){
915 multi_io_send_force(&Net_players[idx]);
916 Net_players[idx].s_info.unreliable_buffer_size = 0;
919 // force reliable data
920 if(Net_players[idx].s_info.reliable_buffer_size > 0){
921 multi_io_send_reliable_force(&Net_players[idx]);
922 Net_players[idx].s_info.reliable_buffer_size = 0;
928 else if(Net_player != NULL){
929 // force unreliable data
930 if(Net_player->s_info.unreliable_buffer_size > 0){
931 multi_io_send_force(Net_player);
932 Net_player->s_info.unreliable_buffer_size = 0;
935 // force reliable data
936 if(Net_player->s_info.reliable_buffer_size > 0){
937 multi_io_send_reliable_force(Net_player);
938 Net_player->s_info.reliable_buffer_size = 0;
943 // 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)
944 void send_game_chat_packet(net_player *from, char *msg, int msg_mode, net_player *to, char *expr, int server_msg)
946 ubyte data[MAX_PACKET_SIZE],mode;
949 BUILD_HEADER(GAME_CHAT);
952 ADD_DATA_S16(from->player_id);
954 // add the message mode and if in MSG_TARGET mode, add who the target is
955 ADD_DATA_S32(server_msg);
956 mode = (ubyte)msg_mode;
959 case MULTI_MSG_TARGET:
961 ADD_DATA_S16(to->player_id);
964 Assert(expr != NULL);
968 // add the message itself
971 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
973 // message all players
975 for(idx=0;idx<MAX_PLAYERS;idx++){
976 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from)){
977 multi_io_send_reliable(&Net_players[idx], data, packet_size);
982 // message only friendly players
983 case MULTI_MSG_FRIENDLY:
984 for(idx=0;idx<MAX_PLAYERS;idx++){
985 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)){
986 multi_io_send_reliable(&Net_players[idx], data, packet_size);
991 // message only hostile players
992 case MULTI_MSG_HOSTILE:
993 for(idx=0;idx<MAX_PLAYERS;idx++){
994 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)){
995 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1000 // message the player's target
1001 case MULTI_MSG_TARGET:
1003 if(MULTI_CONNECTED((*to)) && !MULTI_STANDALONE((*to))){
1004 multi_io_send_reliable(to, data, packet_size);
1008 // message all players who match the expression string
1009 case MULTI_MSG_EXPR:
1010 Assert(expr != NULL);
1011 for(idx=0;idx<MAX_PLAYERS;idx++){
1012 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && multi_msg_matches_expr(&Net_players[idx],expr) ){
1013 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1019 // send to the server, who will take care of routing it
1021 multi_io_send_reliable(Net_player, data, packet_size);
1025 // process a general game chat packet, if we're the standalone we should rebroadcast
1026 void process_game_chat_packet( ubyte *data, header *hinfo )
1030 int color_index,player_index,to_player_index,should_display,server_msg;
1031 char msg[MULTI_MSG_MAX_TEXT_LEN+CALLSIGN_LEN+2];
1035 offset = HEADER_LENGTH;
1037 // get the id of the sender
1040 // determine if this is a server message
1041 GET_DATA_S32(server_msg);
1046 // if targeting a specific player, get the address
1049 case MULTI_MSG_TARGET:
1052 case MULTI_MSG_EXPR:
1056 // get the message itself
1060 // get the index of the sending player
1061 color_index = find_player_id(from);
1062 player_index = color_index;
1064 // if we couldn't find the player - bail
1065 if(player_index == -1){
1066 nprintf(("Network","Could not find player for processing game chat packet!\n"));
1072 // if we're the server, determine what to do with the packet here
1073 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1074 // if he's targeting a specific player, find out who it is
1075 if(mode == MULTI_MSG_TARGET){
1076 to_player_index = find_player_id(to);
1078 to_player_index = -1;
1081 // if we couldn't find who sent the message or who should be getting the message, the bail
1082 if(((to_player_index == -1) && (mode == MULTI_MSG_TARGET)) || (player_index == -1)){
1086 // determine if _I_ should be seeing the text
1087 if(Game_mode & GM_STANDALONE_SERVER){
1090 // check against myself for several specific cases
1092 if((mode == MULTI_MSG_ALL) ||
1093 ((mode == MULTI_MSG_FRIENDLY) && (Net_player->p_info.team == Net_players[player_index].p_info.team)) ||
1094 ((mode == MULTI_MSG_HOSTILE) && (Net_player->p_info.team != Net_players[player_index].p_info.team)) ||
1095 ((mode == MULTI_MSG_TARGET) && (MY_NET_PLAYER_NUM == to_player_index)) ||
1096 ((mode == MULTI_MSG_EXPR) && multi_msg_matches_expr(Net_player,expr)) ){
1101 // if we're the server of a game, we need to rebroadcast to all other players
1103 // individual target mission
1104 case MULTI_MSG_TARGET:
1105 // if I was the inteneded target, or we couldn't find the intended target, don't rebroadcast
1106 if(to_player_index != MY_NET_PLAYER_NUM){
1107 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, &Net_players[to_player_index], NULL, server_msg);
1111 case MULTI_MSG_EXPR:
1112 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, expr, server_msg);
1116 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, NULL, server_msg);
1120 // if a client receives this packet, its always ok for him to display it
1125 // if we're not on a standalone
1127 if(server_msg == 2){
1130 multi_display_chat_msg(msg, player_index, !server_msg);
1135 // broadcast a hud message to all players
1136 void send_hud_msg_to_all( char* msg )
1138 ubyte data[MAX_PACKET_SIZE];
1141 // only the server should be sending this packet
1142 BUILD_HEADER(HUD_MSG);
1146 multi_io_send_to_all( data, packet_size );
1149 // process an incoming hud message packet
1150 void process_hud_message(ubyte* data, header* hinfo)
1153 char msg_buffer[255];
1155 offset = HEADER_LENGTH;
1157 GET_STRING(msg_buffer);
1160 // this is the only safe place to do this since only in the mission is the HUD guaranteed to be inited
1161 if(Game_mode & GM_IN_MISSION){
1162 HUD_printf(msg_buffer);
1166 // send a join packet request to the specified address (should be a server)
1167 void send_join_packet(net_addr* addr,join_request *jr)
1169 ubyte data[MAX_PACKET_SIZE];
1172 // build the header and add the request
1175 add_join_request(data, &packet_size, jr);
1177 psnet_send(addr, data, packet_size);
1180 // process an incoming join request packet
1181 void process_join_packet(ubyte* data, header* hinfo)
1186 int host_restr_mode;
1187 // int team0_avail,team1_avail;
1188 char join_string[255];
1191 // only the server of the game should ever receive this packet
1192 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
1195 offset = HEADER_LENGTH;
1197 // read in the request info
1198 memset(&jr,0,sizeof(join_request));
1201 jr.tracker_id = INTEL_INT(jr.tracker_id);
1205 // fill in the address information of where this came from
1206 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
1208 // determine if we should accept this guy, or return a reason we should reject him
1209 // see the DENY_* codes in multi.h
1210 ret_code = multi_eval_join_request(&jr,&addr);
1212 // evaluate the return code
1214 // he should be accepted
1218 // we have to query the host because this is a restricted game
1219 case JOIN_QUERY_RESTRICTED :
1220 if(!(Game_mode & GM_STANDALONE_SERVER)){
1221 // notify the host of the event
1222 snd_play(&Snds[SND_CUE_VOICE]);
1225 // set the query timestamp
1226 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
1227 Netgame.flags |= NG_FLAG_INGAME_JOINING;
1229 // determine what mode we're in
1230 host_restr_mode = -1;
1231 memset(join_string,0,255);
1232 // if(Netgame.type == NG_TYPE_TEAM){
1233 // multi_player_ships_available(&team0_avail,&team1_avail);
1235 // if(team0_avail && team1_avail){
1236 // host_restr_mode = MULTI_JOIN_RESTR_MODE_4;
1237 // sprintf(join_string,"Player %s has tried to join. Accept on team 1 or 2 ?",jr.callsign);
1238 // } else if(team0_avail && !team1_avail){
1239 // host_restr_mode = MULTI_JOIN_RESTR_MODE_2;
1240 // sprintf(join_string,"Player %s has tried to join team 0, accept y/n ? ?",jr.callsign);
1241 // } else if(!team0_avail && team1_avail){
1242 // host_restr_mode = MULTI_JOIN_RESTR_MODE_3;
1243 // sprintf(join_string,"Player %s has tried to join team 1, accept y/n ?",jr.callsign);
1245 // } else if(Netgame.mode == NG_MODE_RESTRICTED){
1246 host_restr_mode = MULTI_JOIN_RESTR_MODE_1;
1247 sprintf(join_string,XSTR("Player %s has tried to join, accept y/n ?",715),jr.callsign);
1249 Assert(host_restr_mode != -1);
1251 // store the request info
1252 memcpy(&Multi_restr_join_request,&jr,sizeof(join_request));
1253 memcpy(&Multi_restr_addr,&addr,sizeof(net_addr));
1254 Multi_join_restr_mode = host_restr_mode;
1256 // if i'm the standalone server, I need to send a query to the host
1257 if(Game_mode & GM_STANDALONE_SERVER){
1258 send_host_restr_packet(jr.callsign,0,Multi_join_restr_mode);
1260 HUD_printf(join_string);
1264 ml_printf(NOX("Receive restricted join request from %s"), jr.callsign);
1268 // he'e being denied for some reason
1270 // send him the reason he is being denied
1271 send_deny_packet(&addr,ret_code);
1275 // process the rest of the request
1276 multi_process_valid_join_request(&jr,&addr);
1279 // send a notification that a new player has joined the game (if target != NULL, broadcast the packet)
1280 void send_new_player_packet(int new_player_num,net_player *target)
1282 ubyte data[MAX_PACKET_SIZE], val;
1283 int packet_size = 0;
1285 BUILD_HEADER( NOTIFY_NEW_PLAYER );
1287 // add the new player's info
1288 ADD_DATA_S32(new_player_num);
1289 // ADD_DATA(Net_players[new_player_num].p_info.addr);
1291 add_net_addr(data, &packet_size, Net_players[new_player_num].p_info.addr);
1293 ADD_DATA_S16(Net_players[new_player_num].player_id);
1294 ADD_DATA_S32(Net_players[new_player_num].flags);
1295 ADD_STRING(Net_players[new_player_num].player->callsign);
1296 ADD_STRING(Net_players[new_player_num].player->image_filename);
1297 ADD_STRING(Net_players[new_player_num].player->squad_filename);
1298 ADD_STRING(Net_players[new_player_num].p_info.pxo_squad_name);
1300 val = (ubyte)Net_players[new_player_num].p_info.team;
1303 // broadcast the data
1305 multi_io_send_reliable(target, data, packet_size);
1307 multi_io_send_to_all_reliable(data, packet_size);
1311 // process a notification for a new player who has joined the game
1312 void process_new_player_packet(ubyte* data, header* hinfo)
1314 int already_in_game = 0;
1315 int offset, new_player_num,player_num,new_flags;
1317 char new_player_name[CALLSIGN_LEN+2] = "";
1318 char new_player_image[MAX_FILENAME_LEN+1] = "";
1319 char new_player_squad[MAX_FILENAME_LEN+1] = "";
1320 char new_player_pxo_squad[LOGIN_LEN+1] = "";
1321 char notify_string[256];
1325 offset = HEADER_LENGTH;
1327 // get the new players information
1328 GET_DATA_S32(new_player_num);
1329 get_net_addr(data, &offset, new_addr);
1331 GET_DATA_S16(new_id);
1332 GET_DATA_S32(new_flags);
1333 GET_STRING(new_player_name);
1334 GET_STRING(new_player_image);
1335 GET_STRING(new_player_squad);
1336 GET_STRING(new_player_pxo_squad);
1340 player_num = multi_find_open_player_slot();
1341 Assert(player_num != -1);
1343 // note that this new code does not check for duplicate IPs. It merely checks to see if
1344 // the slot referenced by new_player_num is already occupied by a connected player
1345 if(MULTI_CONNECTED(Net_players[new_player_num])){
1349 // if he's not alreayd in the game for one reason or another
1350 if ( !already_in_game ) {
1351 if ( Game_mode & GM_IN_MISSION ){
1352 HUD_sourced_printf(HUD_SOURCE_COMPUTER, XSTR("%s has entered the game\n",716), new_player_name);
1355 // create the player
1356 memcpy(new_addr.net_id, Psnet_my_addr.net_id, 4);
1358 if(new_flags & NETINFO_FLAG_OBSERVER){
1359 multi_obs_create_player(new_player_num,new_player_name,&new_addr,&Players[player_num]);
1360 Net_players[new_player_num].flags |= new_flags;
1362 multi_create_player( new_player_num, &Players[player_num],new_player_name, &new_addr, -1, new_id );
1363 Net_players[new_player_num].flags |= new_flags;
1366 // copy in the filename
1367 if(strlen(new_player_image) > 0){
1368 strcpy(Net_players[new_player_num].player->image_filename, new_player_image);
1370 strcpy(Net_players[new_player_num].player->image_filename, "");
1372 // copy his pilot squad filename
1373 Net_players[new_player_num].player->insignia_texture = -1;
1374 player_set_squad_bitmap(Net_players[new_player_num].player, new_player_squad);
1376 // copy in his pxo squad name
1377 strcpy(Net_players[new_player_num].p_info.pxo_squad_name, new_player_pxo_squad);
1379 // since we just created the player, set the last_heard_time here.
1380 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1382 Net_players[new_player_num].p_info.team = team;
1384 Net_players[new_player_num].player_id = new_id;
1386 // zero out this players ping
1387 multi_ping_reset(&Net_players[new_player_num].s_info.ping);
1389 // add a chat message
1390 if(Net_players[new_player_num].player->callsign != NULL){
1391 sprintf(notify_string,XSTR("<%s has joined>",717),Net_players[new_player_num].player->callsign);
1392 multi_display_chat_msg(notify_string,0,0);
1397 ml_printf(NOX("Received notification of new player %s"), Net_players[new_player_num].player->callsign);
1399 // let the current ui screen know someone joined
1400 switch(gameseq_get_state()){
1401 case GS_STATE_MULTI_HOST_SETUP :
1402 multi_create_handle_join(&Net_players[new_player_num]);
1404 case GS_STATE_MULTI_CLIENT_SETUP :
1405 multi_jw_handle_join(&Net_players[new_player_num]);
1410 #define PLAYER_DATA_SLOP 100
1412 void send_accept_player_data( net_player *npp, int is_ingame )
1416 ubyte data[MAX_PACKET_SIZE], stop;
1418 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1420 // add in the netplayer data for all players
1422 for (i=0; i<MAX_PLAYERS; i++) {
1423 // skip non connected players
1424 if ( !MULTI_CONNECTED(Net_players[i]) ){
1428 // skip this new player's entry
1429 if ( npp->player_id == Net_players[i].player_id ){
1433 // add the stop byte
1436 // add the player's number
1439 // add the player's address
1440 // ADD_DATA(Net_players[i].p_info.addr);
1441 add_net_addr(data, &packet_size, Net_players[i].p_info.addr);
1444 ADD_DATA_S16(Net_players[i].player_id);
1447 ADD_STRING(Net_players[i].player->callsign);
1449 // add his image filename
1450 ADD_STRING(Net_players[i].player->image_filename);
1452 // add his squad filename
1453 ADD_STRING(Net_players[i].player->squad_filename);
1455 // add his PXO squad name
1456 ADD_STRING(Net_players[i].p_info.pxo_squad_name);
1459 ADD_DATA_S32(Net_players[i].flags);
1461 // add his object's net sig
1463 ADD_DATA_U16( Objects[Net_players[i].player->objnum].net_signature );
1466 if ( (packet_size + PLAYER_DATA_SLOP) > MAX_PACKET_SIZE ) {
1467 stop = APD_END_PACKET;
1469 multi_io_send_reliable( npp, data, packet_size );
1470 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1476 // add the stop byte
1477 stop = APD_END_DATA;
1479 multi_io_send_reliable(npp, data, packet_size);
1482 // send an accept packet to a client in response to a request to join the game
1483 void send_accept_packet(int new_player_num, int code, int ingame_join_team)
1486 ubyte data[MAX_PACKET_SIZE],val;
1487 char notify_string[256];
1490 Assert(new_player_num >= 0);
1492 // setup his "reliable" socket
1493 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1495 // build the packet header
1497 BUILD_HEADER(ACCEPT);
1499 // add the accept code
1502 // add code specific accept data
1503 if (code & ACCEPT_INGAME) {
1504 // the game filename
1505 ADD_STRING(Game_current_mission_filename);
1507 // if he is joining on a specific team, mark it here
1508 if(ingame_join_team != -1){
1511 val = (ubyte)ingame_join_team;
1519 if (code & ACCEPT_OBSERVER) {
1520 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1523 if (code & ACCEPT_HOST) {
1524 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1527 if (code & ACCEPT_CLIENT) {
1528 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1531 // add the current skill level setting on the host
1532 ADD_DATA_S32(Game_skill_level);
1534 // add this guys player num
1535 ADD_DATA_S32(new_player_num);
1537 // add his player id
1538 ADD_DATA_S16(Net_players[new_player_num].player_id);
1540 // add netgame type flags
1541 ADD_DATA_S32(Netgame.type_flags);
1544 // char buffer[100];
1545 // nprintf(("Network", "About to send accept packet to %s on port %d\n", get_text_address(buffer, addr->addr), addr->port ));
1548 // actually send the packet
1549 psnet_send(&Net_players[new_player_num].p_info.addr, data, packet_size);
1551 // if he's not an observer, inform all the other players in the game about him
1552 // inform the other players in the game about this new player
1553 for (i=0; i<MAX_PLAYERS; i++) {
1554 // skip unconnected players as well as this new guy himself
1555 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])) {
1559 // send the new packet
1560 send_new_player_packet(new_player_num,&Net_players[i]);
1563 // add a chat message
1564 if(Net_players[new_player_num].player->callsign != NULL){
1565 sprintf(notify_string,XSTR("<%s has joined>",717), Net_players[new_player_num].player->callsign);
1566 multi_display_chat_msg(notify_string, 0, 0);
1569 // handle any team vs. team details
1570 if (!(code & ACCEPT_OBSERVER)) {
1571 multi_team_handle_join(&Net_players[new_player_num]);
1575 if(Net_players[new_player_num].tracker_player_id >= 0){
1576 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);
1578 ml_printf(NOX("Server accepted %s as new client"), Net_players[new_player_num].player->callsign);
1583 // process the player data from the server
1584 void process_accept_player_data( ubyte *data, header *hinfo )
1586 int offset, player_num, player_slot_num, new_flags;
1587 char name[CALLSIGN_LEN + 1] = "";
1588 char image_name[MAX_FILENAME_LEN + 1] = "";
1589 char squad_name[MAX_FILENAME_LEN + 1] = "";
1590 char pxo_squad_name[LOGIN_LEN+1] = "";
1594 ushort ig_signature;
1596 offset = HEADER_LENGTH;
1599 while ( stop == APD_NEXT ) {
1600 player_slot_num = multi_find_open_player_slot();
1601 Assert(player_slot_num != -1);
1603 // get the player's number
1604 GET_DATA_S32(player_num);
1606 // add the player's address
1607 get_net_addr(data, &offset, addr);
1609 // get the player's id#
1610 GET_DATA_S16(player_id);
1615 // add his image filename
1616 GET_STRING(image_name);
1618 // get his squad logo filename
1619 GET_STRING(squad_name);
1621 // get his PXO squad name
1622 GET_STRING(pxo_squad_name);
1625 GET_DATA_S32(new_flags);
1627 if (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) {
1628 if (!multi_obs_create_player(player_num, name, &addr, &Players[player_slot_num])) {
1633 // the error handling here is less than stellar. We should probably put up a popup and go
1634 // back to the main menu. But then again, this should never ever happen!
1635 if ( !multi_create_player(player_num, &Players[player_slot_num],name, &addr, -1, player_id) ) {
1640 // copy his image filename
1641 strcpy(Net_players[player_num].player->image_filename, image_name);
1643 // copy his pilot squad filename
1644 Net_players[player_num].player->insignia_texture = -1;
1645 player_set_squad_bitmap(Net_players[player_num].player, squad_name);
1647 // copy his pxo squad name
1648 strcpy(Net_players[player_num].p_info.pxo_squad_name, pxo_squad_name);
1650 // set his player id#
1651 Net_players[player_num].player_id = player_id;
1653 // mark him as being connected
1654 Net_players[player_num].flags |= NETINFO_FLAG_CONNECTED;
1655 Net_players[player_num].flags |= new_flags;
1657 // set the server pointer
1658 if ( Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER ) {
1659 Netgame.server = &Net_players[player_num];
1660 Netgame.server->last_heard_time = timer_get_fixed_seconds();
1662 // also - always set the server address to be where this data came from, NOT from
1663 // the data in the packet
1664 fill_net_addr(&Net_players[player_num].p_info.addr, hinfo->addr, hinfo->net_id, hinfo->port);
1667 // set the host pointer
1668 if ( Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST ) {
1669 Netgame.host = &Net_players[player_num];
1672 // read in the player's object net signature and store as his objnum for now
1673 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME ) {
1674 GET_DATA_U16( ig_signature );
1675 Net_players[player_num].player->objnum = ig_signature;
1678 // get the stop byte
1683 if ( stop == APD_END_DATA ) {
1684 // if joining a game automatically, set the connect address to NULl so we don't try and
1685 // do this next time we enter a game
1686 if (Cmdline_connect_addr != NULL) {
1687 Cmdline_connect_addr = NULL;
1690 // send my stats to the server if I'm not in observer mode
1691 if (!(Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER)) {
1692 send_player_stats_block_packet(Net_player, STATS_ALLTIME);
1695 // if i'm being accepted as a host, then move into the host setup state
1696 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_HOST) {
1697 // set my permission bits
1698 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
1699 Net_player->state = NETPLAYER_STATE_STD_HOST_SETUP;
1701 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
1704 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER) {
1705 Net_player->flags |= NETINFO_FLAG_OBSERVER;
1707 // since observers can join 1 of 2 ways, only do this if we're not doing an ingame observer join
1708 if ( !(Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) ) {
1709 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1713 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_CLIENT) {
1714 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1717 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) {
1718 // flag myself as being an ingame joiner
1719 Net_player->flags |= NETINFO_FLAG_INGAME_JOIN;
1721 // move myself into the ingame join mission sync state
1722 Multi_sync_mode = MULTI_SYNC_INGAME;
1723 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
1726 // update my options on the server
1727 multi_options_update_local();
1729 // if we're in PXO mode, mark it down in our player struct
1730 if(MULTI_IS_TRACKER_GAME){
1731 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1732 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1737 // process an accept packet from the server
1738 extern int Select_default_ship;
1740 void process_accept_packet(ubyte* data, header* hinfo)
1742 int code, my_player_num, offset;
1746 // get the accept code
1747 offset = HEADER_LENGTH;
1751 // read in the accept code specific data
1753 if (code & ACCEPT_INGAME) {
1754 // the game filename
1755 GET_STRING(Game_current_mission_filename);
1756 Select_default_ship = 0;
1758 // determine if I'm being placed on a team
1765 if (code & ACCEPT_OBSERVER) {
1766 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1769 if (code & ACCEPT_HOST) {
1770 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1773 if (code & ACCEPT_CLIENT) {
1774 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1777 // fill in the netgame server address
1778 fill_net_addr( &Netgame.server_addr, hinfo->addr, hinfo->net_id, hinfo->port );
1780 // get the skill level setting
1781 GET_DATA_S32(Game_skill_level);
1783 // get my netplayer number
1784 GET_DATA_S32(my_player_num);
1787 GET_DATA_S16(player_id);
1789 // get netgame type flags
1790 GET_DATA_S32(Netgame.type_flags);
1792 // setup the Net_players structure for myself first
1793 Net_player = &Net_players[my_player_num];
1794 Net_player->flags = 0;
1795 Net_player->tracker_player_id = Multi_tracker_id;
1796 Net_player->player_id = player_id;
1797 Net_player->s_info.xfer_handle = -1;
1798 // stuff_netplayer_info( Net_player, &Psnet_my_addr, Ships[Objects[Player->objnum].instance].ship_info_index, Player );
1799 stuff_netplayer_info( Net_player, &Psnet_my_addr, 0, Player );
1800 multi_options_local_load(&Net_player->p_info.options, Net_player);
1802 Net_player->p_info.team = team;
1805 // determine if I have a CD
1807 Net_player->flags |= NETINFO_FLAG_HAS_CD;
1810 // set accept code in netplayer for this guy
1811 if ( code & ACCEPT_INGAME ){
1812 Net_player->flags |= NETINFO_FLAG_ACCEPT_INGAME;
1814 if ( code & ACCEPT_OBSERVER ){
1815 Net_player->flags |= NETINFO_FLAG_ACCEPT_OBSERVER;
1817 if ( code & ACCEPT_HOST ){
1818 Net_player->flags |= NETINFO_FLAG_ACCEPT_HOST;
1820 if ( code & ACCEPT_CLIENT ){
1821 Net_player->flags |= NETINFO_FLAG_ACCEPT_CLIENT;
1824 // if I have hacked data
1825 if(game_hacked_data()){
1826 Net_player->flags |= NETINFO_FLAG_HAXOR;
1829 // if we're supposed to flush our local data cache, do so now
1830 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
1831 multi_flush_multidata_cache();
1834 Net_player->sv_bytes_sent = 0;
1835 Net_player->sv_last_pl = -1;
1836 Net_player->cl_bytes_recvd = 0;
1837 Net_player->cl_last_pl = -1;
1839 // intiialize endgame stuff
1840 multi_endgame_init();
1844 // make a call to psnet to initialize and try to connect with the server.
1845 psnet_rel_connect_to_server( &Net_player->reliable_socket, &Netgame.server_addr );
1846 if ( Net_player->reliable_socket == INVALID_SOCKET ) {
1847 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONNECT_FAIL);
1851 // send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet)
1852 void send_leave_game_packet(short player_id, int kicked_reason, net_player *target)
1854 ubyte data[MAX_PACKET_SIZE];
1856 int packet_size = 0;
1858 BUILD_HEADER(LEAVE_GAME);
1860 // add a flag indicating whether he was kicked or not
1861 val = (char)kicked_reason;
1864 if (player_id < 0) {
1865 ADD_DATA_S16(Net_player->player_id);
1867 // inform the host that we are leaving the game
1868 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1869 multi_io_send_to_all_reliable(data, packet_size);
1871 multi_io_send_reliable(Net_player, data, packet_size);
1874 // this is the case where to server is tossing a player (or indicating a respawned player has quit or become an observer)
1875 // so he has to tell everyone that this guy left
1877 nprintf(("Network","Sending a leave game packet to all players (server)\n"));
1879 // a couple of important checks
1880 Assert(player_id != Net_player->player_id);
1881 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1883 // add the id of the guy to be kicked
1884 ADD_DATA_S16(player_id);
1886 // broadcast to everyone
1887 if (target == NULL) {
1888 multi_io_send_to_all_reliable(data, packet_size);
1890 multi_io_send_reliable(target, data, packet_size);
1895 // process a notification the a player has left the game
1896 void process_leave_game_packet(ubyte* data, header* hinfo)
1904 offset = HEADER_LENGTH;
1906 // get whether he was kicked
1907 GET_DATA(kicked_reason);
1909 // get the address of the guy who is to leave
1910 GET_DATA_S16(deader_id);
1913 // determine who is dropping and printf out a notification
1914 player_num = find_player_id(deader_id);
1915 if (player_num == -1) {
1916 nprintf(("Network", "Received leave game packet for unknown player, ignoring\n"));
1920 nprintf(("Network", "Received a leave game notice for %s\n", Net_players[player_num].player->callsign));
1923 // a hook to display that a player was kicked
1924 if (kicked_reason >= 0){
1925 // if it was me that was kicked, leave the game
1926 if((Net_player != NULL) && (Net_player->player_id == deader_id)){
1929 switch(kicked_reason){
1930 case KICK_REASON_BAD_XFER:
1931 notify_code = MULTI_END_NOTIFY_KICKED_BAD_XFER;
1933 case KICK_REASON_CANT_XFER:
1934 notify_code = MULTI_END_NOTIFY_KICKED_CANT_XFER;
1936 case KICK_REASON_INGAME_ENDED:
1937 notify_code = MULTI_END_NOTIFY_KICKED_INGAME_ENDED;
1940 notify_code = MULTI_END_NOTIFY_KICKED;
1944 multi_quit_game(PROMPT_NONE, notify_code);
1947 // otherwise indicate someone was kicked
1949 nprintf(("Network","%s was kicked\n",Net_players[player_num].player->callsign));
1951 // display the result
1952 memset(str, 0, 512);
1953 multi_kick_get_text(&Net_players[player_num], kicked_reason, str);
1954 multi_display_chat_msg(str, player_num, 0);
1958 // first of all, if we're the master, we should be rebroadcasting this packet
1959 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1962 sprintf(msg, XSTR("%s has left the game",719), Net_players[player_num].player->callsign );
1964 if (!(Game_mode & GM_STANDALONE_SERVER)){
1965 HUD_sourced_printf(HUD_SOURCE_HIDDEN, msg);
1968 send_hud_msg_to_all(msg);
1969 multi_io_send_to_all_reliable(data, offset);
1972 // leave the game if the host and/or master has dropped
1974 if (((Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER) || (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)) ) {
1975 nprintf(("Network","Host and/or server has left the game - aborting...\n"));
1978 ml_string(NOX("Host and/or server has left the game"));
1980 // if the host leaves in the debriefing state, we should still wait until the player selects accept before we quit
1981 if (gameseq_get_state() != GS_STATE_DEBRIEF) {
1982 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
1985 delete_player(player_num);
1988 delete_player(player_num);
1990 // OSAPI GUI stuff (if standalone)
1991 if (Game_mode & GM_STANDALONE_SERVER) {
1992 // returns true if we should reset the standalone
1993 if (std_remove_player(&Net_players[player_num])) {
1994 nprintf(("Network", "Should reset!!\n"));
1998 // update these gui vals
1999 std_connect_set_host_connect_status();
2000 std_connect_set_connect_count();
2004 // send information about this currently active game to the specified address
2005 void send_game_active_packet(net_addr* addr)
2009 ubyte data[MAX_PACKET_SIZE],val;
2011 // build the header and add the data
2012 BUILD_HEADER(GAME_ACTIVE);
2014 // add the server version and compatible version #
2015 val = MULTI_FS_SERVER_VERSION;
2017 val = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2020 ADD_STRING(Netgame.name);
2021 ADD_STRING(Netgame.mission_name);
2022 ADD_STRING(Netgame.title);
2023 val = (ubyte)multi_num_players();
2026 // add the proper flags
2028 if((Netgame.mode == NG_MODE_PASSWORD) || ((Game_mode & GM_STANDALONE_SERVER) && (multi_num_players() == 0) && (std_is_host_passwd()))){
2029 flags |= AG_FLAG_PASSWD;
2032 // proper netgame type flags
2033 if(Netgame.type_flags & NG_TYPE_TEAM){
2034 flags |= AG_FLAG_TEAMS;
2035 } else if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
2036 flags |= AG_FLAG_DOGFIGHT;
2038 flags |= AG_FLAG_COOP;
2041 // proper netgame state flags
2042 switch(Netgame.game_state){
2043 case NETGAME_STATE_FORMING:
2044 flags |= AG_FLAG_FORMING;
2047 case NETGAME_STATE_BRIEFING:
2048 case NETGAME_STATE_MISSION_SYNC:
2049 case NETGAME_STATE_HOST_SETUP:
2050 flags |= AG_FLAG_BRIEFING;
2053 case NETGAME_STATE_IN_MISSION:
2054 flags |= AG_FLAG_IN_MISSION;
2057 case NETGAME_STATE_PAUSED:
2058 flags |= AG_FLAG_PAUSE;
2061 case NETGAME_STATE_ENDGAME:
2062 case NETGAME_STATE_DEBRIEF:
2063 flags |= AG_FLAG_DEBRIEF;
2067 // if this is a standalone
2068 if(Game_mode & GM_STANDALONE_SERVER){
2069 flags |= AG_FLAG_STANDALONE;
2072 // if we're in campaign mode
2073 if(Netgame.campaign_mode == MP_CAMPAIGN){
2074 flags |= AG_FLAG_CAMPAIGN;
2077 // add the data about the connection speed of the host machine
2078 Assert( (Multi_connection_speed >= 0) && (Multi_connection_speed <= 4) );
2079 flags |= (Multi_connection_speed << AG_FLAG_CONNECTION_BIT);
2081 ADD_DATA_U16(flags);
2084 psnet_send(addr, data, packet_size);
2087 // process information about an active game
2088 void process_game_active_packet(ubyte* data, header* hinfo)
2093 int modes_compatible;
2095 fill_net_addr(&ag.server_addr, hinfo->addr, hinfo->net_id, hinfo->port);
2097 // read this game into a temporary structure
2098 offset = HEADER_LENGTH;
2100 // get the server version and compatible version
2101 GET_DATA(ag.version);
2102 GET_DATA(ag.comp_version);
2104 GET_STRING(ag.name);
2105 GET_STRING(ag.mission_name);
2106 GET_STRING(ag.title);
2108 ag.num_players = val;
2109 GET_DATA_U16(ag.flags);
2113 modes_compatible = 1;
2115 if((ag.flags & AG_FLAG_TRACKER) && !Multi_options_g.pxo){
2116 modes_compatible = 0;
2118 if(!(ag.flags & AG_FLAG_TRACKER) && Multi_options_g.pxo){
2119 modes_compatible = 0;
2123 // if this is a compatible version, and our modes are compatible, register it
2124 if( (ag.version == MULTI_FS_SERVER_VERSION) && modes_compatible ){
2125 multi_update_active_games(&ag);
2129 // send_game_update_packet sends an updated Netgame structure to all players currently connected. The update
2130 // is used to change the current mission, current state, etc.
2131 void send_netgame_update_packet(net_player *pl)
2135 ubyte data[MAX_PACKET_SIZE];
2138 BUILD_HEADER(GAME_UPDATE);
2140 // with new mission description field, this becomes way to large
2141 // so we must add every element piece by piece except the
2142 ADD_STRING(Netgame.name);
2143 ADD_STRING(Netgame.mission_name);
2144 ADD_STRING(Netgame.title);
2145 ADD_STRING(Netgame.campaign_name);
2146 ADD_DATA_S32(Netgame.campaign_mode);
2147 ADD_DATA_S32(Netgame.max_players);
2148 ADD_DATA_S32(Netgame.security);
2149 ADD_DATA_U32(Netgame.respawn);
2150 ADD_DATA_S32(Netgame.flags);
2151 ADD_DATA_S32(Netgame.type_flags);
2152 ADD_DATA_S32(Netgame.version_info);
2153 ADD_DATA(Netgame.debug_flags);
2155 // only the server should ever send the netgame state (standalone situation)
2156 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2157 ADD_DATA_S32(Netgame.game_state);
2160 // if we're the host on a standalone, send to the standalone and let him rebroadcast
2161 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2163 multi_io_send_to_all_reliable(data, packet_size);
2165 for(idx=0; idx<MAX_PLAYERS; idx++){
2166 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
2167 send_netgame_descript_packet(&Net_players[idx].p_info.addr, 1);
2171 multi_io_send_reliable(pl, data, packet_size);
2172 send_netgame_descript_packet( &pl->p_info.addr , 1 );
2175 Assert( pl == NULL ); // I don't think that a host in a standalone game would get here.
2176 multi_io_send_reliable(Net_player, data, packet_size);
2179 // host should always send a netgame options update as well
2180 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2181 multi_options_update_netgame();
2185 // process information about the netgame sent from the server/host
2186 void process_netgame_update_packet( ubyte *data, header *hinfo )
2188 int offset,old_flags;
2191 Assert(!(Game_mode & GM_STANDALONE_SERVER));
2192 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
2194 // read in the netgame information
2195 offset = HEADER_LENGTH;
2196 GET_STRING(Netgame.name);
2197 GET_STRING(Netgame.mission_name);
2198 GET_STRING(Netgame.title);
2199 GET_STRING(Netgame.campaign_name);
2200 GET_DATA_S32(Netgame.campaign_mode);
2201 GET_DATA_S32(Netgame.max_players); // ignore on the standalone, who keeps track of this himself
2202 GET_DATA_S32(Netgame.security);
2203 GET_DATA_U32(Netgame.respawn);
2205 // be sure not to blast the quitting flag because of the "one frame extra" problem
2206 old_flags = Netgame.flags;
2207 GET_DATA_S32(Netgame.flags);
2208 GET_DATA_S32(Netgame.type_flags);
2209 GET_DATA_S32(Netgame.version_info);
2210 GET_DATA(Netgame.debug_flags);
2213 GET_DATA_S32(ng_state);
2217 // now compare the passed in game state to our current known state. If it has changed, then maybe
2218 // do something interesting.
2219 // move from the forming or debriefing state to the mission sync state
2220 if ( ng_state == NETGAME_STATE_MISSION_SYNC ){
2221 // if coming from the forming state
2222 if( (Netgame.game_state == NETGAME_STATE_FORMING) ||
2223 ((Netgame.game_state != NETGAME_STATE_FORMING) && ((gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP) || (gameseq_get_state() == GS_STATE_MULTI_CLIENT_SETUP))) ){
2224 // do any special processing for forced state transitions
2225 multi_handle_state_special();
2227 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2228 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2229 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2231 // if coming from the debriefing state
2232 else if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2233 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2235 // do any special processing for forced state transitions
2236 multi_handle_state_special();
2238 multi_flush_mission_stuff();
2240 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2241 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2242 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2245 // move from mission sync to team select
2246 else if ( ng_state == NETGAME_STATE_BRIEFING ){
2247 if( (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ||
2248 ((Netgame.game_state != NETGAME_STATE_MISSION_SYNC) && (gameseq_get_state() == GS_STATE_MULTI_MISSION_SYNC) && (Multi_sync_mode != MULTI_SYNC_POST_BRIEFING)) ){
2250 // do any special processing for forced state transitions
2251 multi_handle_state_special();
2253 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2254 gameseq_post_event(GS_EVENT_START_BRIEFING);
2257 // move from the debriefing to the create game screen
2258 else if ( ng_state == NETGAME_STATE_FORMING ){
2259 if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2260 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2261 // do any special processing for forced state transitions
2262 multi_handle_state_special();
2264 multi_flush_mission_stuff();
2266 // move to the proper screen
2267 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2268 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2270 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
2275 Netgame.game_state = ng_state;
2278 // send a request or a reply for mission description, if code == 0, request, if code == 1, reply
2279 void send_netgame_descript_packet(net_addr *addr, int code)
2281 ubyte data[MAX_PACKET_SIZE],val;
2283 int packet_size = 0;
2286 BUILD_HEADER(UPDATE_DESCRIPT);
2292 // add as much of the description as we dare
2293 len = strlen(The_mission.mission_desc);
2294 if(len > MAX_PACKET_SIZE - 10){
2295 len = MAX_PACKET_SIZE - 10;
2297 memcpy(data+packet_size,The_mission.mission_desc,len);
2300 ADD_STRING(The_mission.mission_desc);
2304 Assert(addr != NULL);
2306 psnet_send(addr, data, packet_size);
2310 // process an incoming netgame description packet
2311 void process_netgame_descript_packet( ubyte *data, header *hinfo )
2315 char mission_desc[MISSION_DESC_LENGTH+2];
2318 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2320 // read this game into a temporary structure
2321 offset = HEADER_LENGTH;
2324 // if this is a request for mission description
2326 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2331 // send an update to this guy
2332 send_netgame_descript_packet(&addr, 1);
2334 memset(mission_desc,0,MISSION_DESC_LENGTH+2);
2335 GET_STRING(mission_desc);
2337 // only display if we're in the proper state
2338 state = gameseq_get_state();
2340 case GS_STATE_MULTI_JOIN_GAME:
2341 case GS_STATE_MULTI_CLIENT_SETUP:
2342 case GS_STATE_MULTI_HOST_SETUP:
2343 multi_common_set_text(mission_desc);
2351 // broadcast a query for active games. IPX will use net broadcast and TCP will either request from the MT or from the specified list
2352 void broadcast_game_query()
2356 server_item *s_moveup;
2357 ubyte data[MAX_PACKET_SIZE];
2359 BUILD_HEADER(GAME_QUERY);
2361 // go through the server list and query each of those as well
2362 s_moveup = Game_server_head;
2363 if(s_moveup != NULL){
2365 send_server_query(&s_moveup->server_addr);
2366 s_moveup = s_moveup->next;
2367 } while(s_moveup != Game_server_head);
2370 fill_net_addr(&addr, Psnet_my_addr.addr, Psnet_my_addr.net_id, DEFAULT_GAME_PORT);
2372 // send out a broadcast if our options allow us
2373 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2374 psnet_broadcast( &addr, data, packet_size);
2378 // send an individual query to an address to see if there is an active game
2379 void send_server_query(net_addr *addr)
2382 ubyte data[MAX_PACKET_SIZE];
2384 // build the header and send the data
2385 BUILD_HEADER(GAME_QUERY);
2386 psnet_send(addr, data, packet_size);
2389 // process a query from a client looking for active freespace games
2390 void process_game_query(ubyte* data, header* hinfo)
2395 offset = HEADER_LENGTH;
2399 // check to be sure that we don't capture our own broadcast message
2400 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2401 if ( psnet_same( &addr, &Psnet_my_addr) ){
2405 // if I am not a server of a game, don't send a reply!!!
2406 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
2410 // if the game options are being selected, then ignore the request
2411 // also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
2412 if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
2416 // send information about this active game
2417 send_game_active_packet(&addr);
2420 // sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
2421 void send_netplayer_update_packet( net_player *pl )
2423 int packet_size,idx;
2424 ubyte data[MAX_PACKET_SIZE],val;
2426 BUILD_HEADER(NETPLAYER_UPDATE);
2428 // if I'm the server of the game, I should send an update for _all_players in the game
2429 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2430 for(idx=0;idx<MAX_PLAYERS;idx++){
2431 // only send info for connected players
2432 if(MULTI_CONNECTED(Net_players[idx])){
2437 // add the net player's information
2438 ADD_DATA_S16(Net_players[idx].player_id);
2439 ADD_DATA_S32(Net_players[idx].state);
2440 ADD_DATA_S32(Net_players[idx].p_info.ship_class);
2441 ADD_DATA_S32(Net_players[idx].tracker_player_id);
2443 if(Net_players[idx].flags & NETINFO_FLAG_HAS_CD){
2451 // add the final stop byte
2455 // broadcast the packet
2456 if(!(Game_mode & GM_IN_MISSION)){
2458 multi_io_send_to_all_reliable(data, packet_size);
2460 multi_io_send_reliable(pl, data, packet_size);
2464 multi_io_send_to_all(data, packet_size);
2466 multi_io_send(pl, data, packet_size);
2474 // add my current state in the netgame to this packet
2475 ADD_DATA_S16(Net_player->player_id);
2476 ADD_DATA_S32(Net_player->state);
2477 ADD_DATA_S32(Net_player->p_info.ship_class);
2478 ADD_DATA_S32(Multi_tracker_id);
2480 // add if I have a CD or not
2488 // add a final stop byte
2492 // send the packet to the server
2493 Assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
2494 if(!(Game_mode & GM_IN_MISSION)){
2495 multi_io_send_reliable(Net_player, data, packet_size);
2497 multi_io_send(Net_player, data, packet_size);
2502 // process an incoming netplayer state update. if we're the server, we should rebroadcast
2503 void process_netplayer_update_packet( ubyte *data, header *hinfo )
2505 int offset, player_num;
2511 offset = HEADER_LENGTH;
2513 // get the first stop byte
2516 while(stop != 0xff){
2517 // look the player up
2518 GET_DATA_S16(player_id);
2519 player_num = find_player_id(player_id);
2520 // if we couldn't find him, read in the bogus data
2521 if((player_num == -1) || (Net_player == &Net_players[player_num])){
2522 GET_DATA_S32(bogus.state);
2523 GET_DATA_S32(bogus.p_info.ship_class);
2524 GET_DATA_S32(bogus.tracker_player_id);
2528 // otherwise read in the data correctly
2530 GET_DATA_S32(new_state);
2531 GET_DATA_S32(Net_players[player_num].p_info.ship_class);
2532 GET_DATA_S32(Net_players[player_num].tracker_player_id);
2535 Net_players[player_num].flags |= NETINFO_FLAG_HAS_CD;
2537 Net_players[player_num].flags &= ~(NETINFO_FLAG_HAS_CD);
2540 // if he's changing state to joined, send a team update
2541 if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
2542 multi_team_send_update();
2546 Net_players[player_num].state = new_state;
2549 // get the next stop byte
2555 // if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
2556 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2557 send_netplayer_update_packet(NULL);
2560 // if i'm the standalone and this is an update from the host, maybe change some netgame settings
2561 if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
2562 switch(Net_players[player_num].state){
2563 case NETPLAYER_STATE_STD_HOST_SETUP:
2564 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2567 case NETPLAYER_STATE_HOST_SETUP:
2568 // check for race conditions
2569 if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
2570 Netgame.game_state = NETGAME_STATE_FORMING;
2577 #define EXTRA_DEATH_VAPORIZED (1<<0)
2578 #define EXTRA_DEATH_WASHED (1<<1)
2579 // send a packet indicating a ship has been killed
2580 void send_ship_kill_packet( object *objp, object *other_objp, float percent_killed, int self_destruct )
2582 int packet_size, model;
2583 ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
2584 ushort debris_signature;
2588 // only sendable from the master
2589 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
2592 vaporized = ( (Ships[objp->instance].flags & SF_VAPORIZE) > 0 );
2594 extra_death_info = 0;
2596 extra_death_info |= EXTRA_DEATH_VAPORIZED;
2599 if ( Ships[objp->instance].wash_killed ) {
2600 extra_death_info |= EXTRA_DEATH_WASHED;
2603 // find out the next network signature that will be used for the debris pieces.
2604 model = Ships[objp->instance].modelnum;
2605 pm = model_get(model);
2606 debris_signature = 0;
2607 if ( pm && !vaporized ) {
2608 debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
2609 multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
2610 Ships[objp->instance].arrival_distance = debris_signature;
2613 BUILD_HEADER(SHIP_KILL);
2614 ADD_DATA_U16(objp->net_signature);
2616 // ships which are initially killed get the rest of the data sent. self destructed ships and
2617 if ( other_objp == NULL ) {
2622 nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
2624 ADD_DATA_U16( other_objp->net_signature );
2627 ADD_DATA_U16( debris_signature );
2628 ADD_DATA_FL( percent_killed );
2629 sd = (ubyte)self_destruct;
2631 ADD_DATA( extra_death_info );
2633 // if the ship who died is a player, then send some extra info, like who killed him, etc.
2635 if ( objp->flags & OF_PLAYER_SHIP ) {
2639 pnum = multi_find_player_by_object( objp );
2642 ADD_DATA( was_player );
2644 Assert(Net_players[pnum].player->killer_objtype < CHAR_MAX);
2645 temp = (char)Net_players[pnum].player->killer_objtype;
2648 Assert(Net_players[pnum].player->killer_species < CHAR_MAX);
2649 temp = (char)Net_players[pnum].player->killer_species;
2652 Assert(Net_players[pnum].player->killer_weapon_index < CHAR_MAX);
2653 temp = (char)Net_players[pnum].player->killer_weapon_index;
2656 ADD_STRING( Net_players[pnum].player->killer_parent_name );
2658 ADD_DATA( was_player );
2661 ADD_DATA( was_player );
2664 // send the packet reliably!!!
2665 multi_io_send_to_all_reliable(data, packet_size);
2668 // process a packet indicating that a ship has been killed
2669 void process_ship_kill_packet( ubyte *data, header *hinfo )
2672 ushort ship_sig, other_sig, debris_sig;
2673 object *sobjp, *oobjp;
2674 float percent_killed;
2675 ubyte was_player, extra_death_info, sd;
2676 char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = SPECIES_TERRAN, killer_weapon_index = -1;
2678 offset = HEADER_LENGTH;
2679 GET_DATA_U16(ship_sig);
2681 GET_DATA_U16( other_sig );
2682 GET_DATA_U16( debris_sig );
2683 GET_DATA_FL( percent_killed );
2685 GET_DATA( extra_death_info );
2686 GET_DATA( was_player );
2689 // pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
2690 if ( was_player != 0 ) {
2691 GET_DATA( killer_objtype );
2692 GET_DATA( killer_species );
2693 GET_DATA( killer_weapon_index );
2694 GET_STRING( killer_name );
2699 sobjp = multi_get_network_object( ship_sig );
2701 // if I am unable to find the ship object which was killed, I have to bail and rely on getting
2702 // another message from the server that this happened!
2703 if ( sobjp == NULL ) {
2704 nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
2708 // set this ship's hull value to 0
2709 sobjp->hull_strength = 0.0f;
2711 // maybe set vaporized
2712 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2713 Ships[sobjp->instance].flags |= SF_VAPORIZE;
2716 // maybe set wash_killed
2717 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2718 Ships[sobjp->instance].wash_killed = 1;
2721 oobjp = multi_get_network_object( other_sig );
2723 if ( was_player != 0 ) {
2726 pnum = multi_find_player_by_object( sobjp );
2728 Net_players[pnum].player->killer_objtype = killer_objtype;
2729 Net_players[pnum].player->killer_species = killer_species;
2730 Net_players[pnum].player->killer_weapon_index = killer_weapon_index;
2731 strcpy( Net_players[pnum].player->killer_parent_name, killer_name );
2735 // check to see if I need to respawn myself
2736 multi_respawn_check(sobjp);
2738 // store the debris signature in the arrival distance which will never get used for player ships
2739 Ships[sobjp->instance].arrival_distance = debris_sig;
2741 // set this bit so that we don't accidentally start switching targets when we die
2742 if(sobjp == Player_obj){
2743 Game_mode |= GM_DEAD_DIED;
2746 nprintf(("Network", "Killing off %s\n", Ships[sobjp->instance].ship_name));
2748 // do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
2749 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2750 ship_hit_kill( sobjp, oobjp, percent_killed, sd );
2752 extern void ship_destroyed( int shipnum );
2753 ship_destroyed( sobjp->instance );
2754 sobjp->flags |= OF_SHOULD_BE_DEAD;
2755 obj_delete( OBJ_INDEX(sobjp) );
2759 // send a packet indicating a ship should be created
2760 void send_ship_create_packet( object *objp, int is_support )
2763 ubyte data[MAX_PACKET_SIZE];
2765 // We will pass the ship to create by name.
2766 BUILD_HEADER(SHIP_CREATE);
2767 ADD_DATA_U16(objp->net_signature);
2768 ADD_DATA_S32( is_support );
2770 add_vector_data(data, &packet_size, objp->pos);
2773 // broadcast the packet
2774 multi_io_send_to_all_reliable(data, packet_size);
2777 // process a packet indicating a ship should be created
2778 void process_ship_create_packet( ubyte *data, header *hinfo )
2780 int offset, objnum, is_support;
2783 vector pos = ZERO_VECTOR;
2785 Assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
2786 offset = HEADER_LENGTH;
2787 GET_DATA_U16(signature);
2788 GET_DATA_S32( is_support );
2790 get_vector_data(data, &offset, pos);
2795 // find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
2796 if ( !is_support ) {
2797 objp = mission_parse_get_arrival_ship( signature );
2798 if ( objp != NULL ) {
2799 objnum = parse_create_object(objp);
2801 nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
2804 Assert( Arriving_support_ship );
2805 if(Arriving_support_ship == NULL){
2808 Arriving_support_ship->pos = pos;
2809 Arriving_support_ship->net_signature = signature;
2810 objnum = parse_create_object( Arriving_support_ship );
2811 Assert( objnum != -1 );
2813 mission_parse_support_arrived( objnum );
2818 // send a packet indicating a wing of ships should be created
2819 void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
2821 int packet_size, index, ship_instance;
2822 ubyte data[MAX_PACKET_SIZE];
2826 // for creating wing -- we just send the index into the wing array of this wing.
2827 // all players load the same mission, and so their array's should all match. We also
2828 // need to send the signature of the first ship that was created. We can find this by
2829 // looking num_to_create places back in the ship_index field in the wing structure.
2831 index = WING_INDEX(wingp);
2832 ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
2833 signature = Objects[Ships[ship_instance].objnum].net_signature;
2835 BUILD_HEADER( WING_CREATE );
2836 ADD_DATA_S32(index);
2837 ADD_DATA_S32(num_to_create);
2838 ADD_DATA_U16(signature);
2839 ADD_DATA_S32(pre_create_count);
2840 val = wingp->current_wave - 1;
2843 multi_io_send_to_all_reliable(data, packet_size);
2846 // process a packet saying that a wing should be created
2847 void process_wing_create_packet( ubyte *data, header *hinfo )
2849 int offset, index, num_to_create;
2851 int total_arrived_count, current_wave;
2853 offset = HEADER_LENGTH;
2854 GET_DATA_S32(index);
2855 GET_DATA_S32(num_to_create);
2856 GET_DATA_U16(signature);
2857 GET_DATA_S32(total_arrived_count);
2858 GET_DATA_S32(current_wave);
2862 // do a sanity check on the wing to be sure that we are actually working on a valid wing
2863 if ( (index < 0) || (index >= num_wings) || (Wings[index].num_waves == -1) ) {
2864 nprintf(("Network", "invalid index %d for wing create packet\n"));
2867 if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
2868 nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
2873 Wings[index].current_count = 0;
2874 Wings[index].total_arrived_count = total_arrived_count;
2875 Wings[index].current_wave = current_wave;
2877 // set the network signature that was passed. The client should create ships in the same order
2878 // as the server -- so all ships should get the same sigs as assigned by the server. We also
2879 // need to set some timestamps and cues correctly to be sure that these things get created on
2880 // the clients correctly
2881 multi_set_network_signature( signature, MULTI_SIG_SHIP );
2882 parse_wing_create_ships( &Wings[index], num_to_create, 1 );
2885 // packet indicating a ship is departing
2886 void send_ship_depart_packet( object *objp )
2888 ubyte data[MAX_PACKET_SIZE];
2892 signature = objp->net_signature;
2894 BUILD_HEADER(SHIP_DEPART);
2895 ADD_DATA_U16( signature );
2897 multi_io_send_to_all_reliable(data, packet_size);
2900 // process a packet indicating a ship is departing
2901 void process_ship_depart_packet( ubyte *data, header *hinfo )
2907 offset = HEADER_LENGTH;
2908 GET_DATA_U16( signature );
2911 // find the object which is departing
2912 objp = multi_get_network_object( signature );
2913 if ( objp == NULL ) {
2914 nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
2918 // start warping him out
2919 shipfx_warpout_start( objp );
2922 // packet to tell clients cargo of a ship was revealed to all
2923 void send_cargo_revealed_packet( ship *shipp )
2925 ubyte data[MAX_PACKET_SIZE];
2928 // build the header and add the data
2929 BUILD_HEADER(CARGO_REVEALED);
2930 ADD_DATA_U16( Objects[shipp->objnum].net_signature );
2932 // server sends to all players
2933 if(MULTIPLAYER_MASTER){
2934 multi_io_send_to_all_reliable(data, packet_size);
2936 // clients just send to the server
2938 multi_io_send_reliable(Net_player, data, packet_size);
2942 // process a cargo revealed packet
2943 void process_cargo_revealed_packet( ubyte *data, header *hinfo )
2949 offset = HEADER_LENGTH;
2950 GET_DATA_U16(signature);
2953 // get a ship pointer and call the ship function to reveal the cargo
2954 objp = multi_get_network_object( signature );
2955 if ( objp == NULL ) {
2956 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
2960 // Assert( objp->type == OBJ_SHIP );
2961 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2965 // this will take care of re-routing to all other clients
2966 ship_do_cargo_revealed( &Ships[objp->instance], 1);
2968 // server should rebroadcast
2969 if(MULTIPLAYER_MASTER){
2970 send_cargo_revealed_packet(&Ships[objp->instance]);
2974 // defines used for secondary fire packet
2975 #define SFPF_ALLOW_SWARM (1<<7)
2976 #define SFPF_DUAL_FIRE (1<<6)
2977 #define SFPF_TARGET_LOCKED (1<<5)
2979 // send a packet indicating a secondary weapon was fired
2980 void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm )
2982 int packet_size, net_player_num;
2983 ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
2985 ushort target_signature;
2989 // Assert ( starting_count < UCHAR_MAX );
2991 // get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
2992 // we might send the info to the other player different than the one who fired
2993 objp = &Objects[shipp->objnum];
2994 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
2995 if ( num_fired == 0 ) {
3000 aip = &Ai_info[shipp->ai_index];
3002 current_bank = (ubyte)shipp->weapons.current_secondary_bank;
3003 //Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3005 // build up the header portion
3006 BUILD_HEADER( SECONDARY_FIRED_AI );
3008 ADD_DATA_U16( Objects[shipp->objnum].net_signature );
3009 ADD_DATA_U16( starting_sig );
3011 // add a couple of bits for swarm missiles and dual fire secondary weaspons
3014 sinfo = current_bank;
3017 sinfo |= SFPF_ALLOW_SWARM;
3020 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ){
3021 sinfo |= SFPF_DUAL_FIRE;
3024 if ( aip->current_target_is_locked ){
3025 sinfo |= SFPF_TARGET_LOCKED;
3030 // add the ship's target and any targeted subsystem
3031 target_signature = 0;
3033 if ( aip->target_objnum != -1) {
3034 target_signature = Objects[aip->target_objnum].net_signature;
3035 if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
3038 s_index = ship_get_index_from_subsys( aip->targeted_subsys, aip->target_objnum );
3039 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
3040 t_subsys = (char)s_index;
3043 if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
3044 Assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags & WIF_BOMB);
3049 ADD_DATA_U16( target_signature );
3050 ADD_DATA( t_subsys );
3052 // just send this packet to everyone, then bail if an AI ship fired.
3053 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
3054 multi_io_send_to_all(data, packet_size);
3058 net_player_num = multi_find_player_by_object( objp );
3060 // getting here means a player fired. Send the current packet to all players except the player
3061 // who fired. If nothing got fired, then don't send to the other players -- we will just send
3062 // a packet to the player who will find out that he didn't fire anything
3063 if ( num_fired > 0 ) {
3064 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
3067 // if I (the master) fired, then return
3068 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
3072 // now build up the packet to send to the player who actually fired.
3073 BUILD_HEADER( SECONDARY_FIRED_PLR );
3074 ADD_DATA_U16(starting_sig);
3077 // add the targeting information so that the player's weapons will always home on the correct
3079 ADD_DATA_U16( target_signature );
3080 ADD_DATA( t_subsys );
3082 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
3085 /// process a packet indicating a secondary weapon was fired
3086 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
3088 int offset, allow_swarm, target_objnum_save;
3089 ushort net_signature, starting_sig, target_signature;
3090 ubyte sinfo, current_bank;
3091 object* objp, *target_objp;
3095 ship_subsys *targeted_subsys_save;
3097 offset = HEADER_LENGTH; // size of the header
3099 // if from_player is false, it means that the secondary weapon info in this packet was
3100 // fired by an ai object (or another player). from_player == 1 means tha me (the person
3101 // receiving this packet) fired the secondary weapon
3102 if ( !from_player ) {
3103 GET_DATA_U16( net_signature );
3104 GET_DATA_U16( starting_sig );
3105 GET_DATA( sinfo ); // are we firing swarm missiles
3107 GET_DATA_U16( target_signature );
3108 GET_DATA( t_subsys );
3112 // find the object (based on network signatures) for the object that fired
3113 objp = multi_get_network_object( net_signature );
3114 if ( objp == NULL ) {
3115 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3119 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3120 // of the next fire time of this bank to 0 so we can fire right away
3121 shipp = &Ships[objp->instance];
3124 GET_DATA_U16( starting_sig );
3127 GET_DATA_U16( target_signature );
3128 GET_DATA( t_subsys );
3132 // get the object and ship
3134 shipp = Player_ship;
3137 // check the allow swarm bit
3139 if ( sinfo & SFPF_ALLOW_SWARM ){
3143 // set the dual fire properties of the ship
3144 if ( sinfo & SFPF_DUAL_FIRE ){
3145 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3147 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3150 // determine whether current target is locked
3151 Assert( shipp->ai_index != -1 );
3152 aip = &Ai_info[shipp->ai_index];
3153 if ( sinfo & SFPF_TARGET_LOCKED ) {
3154 aip->current_target_is_locked = 1;
3156 aip->current_target_is_locked = 0;
3159 // find out the current bank
3160 current_bank = (ubyte)(sinfo & 0x3);
3161 //Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3162 shipp->weapons.current_secondary_bank = current_bank;
3164 // make it so we can fire this ship's secondary bank immediately!!!
3165 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3166 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3168 // set this ship's target and subsystem information. We will save and restore target and
3169 // targeted subsystem so that we do not accidentally change targets for this player or
3170 // any AI ships on his system.
3171 target_objnum_save = aip->target_objnum;
3172 targeted_subsys_save = aip->targeted_subsys;
3174 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3175 aip->target_objnum = -1;
3176 aip->targeted_subsys = NULL;
3178 target_objp = multi_get_network_object( target_signature );
3179 if ( target_objp != NULL ) {
3180 aip->target_objnum = OBJ_INDEX(target_objp);
3182 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3183 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3187 if ( starting_sig != 0 ){
3188 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3190 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3193 ship_fire_secondary( objp, allow_swarm );
3195 // restore targeted object and targeted subsystem
3196 aip->target_objnum = target_objnum_save;
3197 aip->targeted_subsys = targeted_subsys_save;
3200 // send a packet indicating a countermeasure was fired
3201 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3203 ubyte data[MAX_PACKET_SIZE];
3208 Assert ( cmeasure_count < UCHAR_MAX );
3209 BUILD_HEADER(COUNTERMEASURE_FIRED);
3210 ADD_DATA_U16( objp->net_signature );
3211 ADD_DATA_S32( rand_val );
3213 multi_io_send_to_all(data, packet_size);
3216 // process a packet indicating a countermeasure was fired
3217 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3219 int offset, rand_val;
3225 offset = HEADER_LENGTH;
3227 GET_DATA_U16( signature );
3228 GET_DATA_S32( rand_val );
3231 objp = multi_get_network_object( signature );
3232 if ( objp == NULL ) {
3233 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3236 if(objp->type != OBJ_SHIP){
3239 // Assert ( objp->type == OBJ_SHIP );
3241 // make it so ship can fire right away!
3242 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3243 if ( objp == Player_obj ){
3244 nprintf(("network", "firing countermeasure from my ship\n"));
3247 ship_launch_countermeasure( objp, rand_val );
3250 // send a packet indicating that a turret has been fired
3251 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3254 ushort pnet_signature;
3255 ubyte data[MAX_PACKET_SIZE], cindex;
3262 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3266 // local setup -- be sure we are actually passing a weapon!!!!
3267 objp = &Objects[weapon_objnum];
3268 Assert ( objp->type == OBJ_WEAPON );
3269 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3273 pnet_signature = Objects[ship_objnum].net_signature;
3275 Assert( subsys_index < UCHAR_MAX );
3276 cindex = (ubyte)subsys_index;
3278 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3283 // build the fire turret packet.
3284 BUILD_HEADER(FIRE_TURRET_WEAPON);
3285 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
3286 ADD_DATA( has_sig );
3287 ADD_DATA_U16( pnet_signature );
3289 ADD_DATA_U16( objp->net_signature );
3292 val = (short)ssp->submodel_info_1.angs.h;
3293 ADD_DATA_S16( val );
3294 val = (short)ssp->submodel_info_2.angs.p;
3295 ADD_DATA_S16( val );
3297 multi_io_send_to_all(data, packet_size);
3299 multi_rate_add(1, "tur", packet_size);
3302 // process a packet indicating a turret has been fired
3303 void process_turret_fired_packet( ubyte *data, header *hinfo )
3305 int offset, weapon_objnum, wid;
3306 ushort pnet_signature, wnet_signature;
3315 short pitch, heading;
3317 // get the data for the turret fired packet
3318 offset = HEADER_LENGTH;
3319 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3320 GET_DATA( has_sig );
3321 GET_DATA_U16( pnet_signature );
3323 GET_DATA_U16( wnet_signature );
3327 GET_DATA( turret_index );
3328 GET_DATA_S16( heading );
3329 GET_DATA_S16( pitch );
3330 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3333 objp = multi_get_network_object( pnet_signature );
3334 if ( objp == NULL ) {
3335 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3339 // if this isn't a ship, do nothing
3340 if ( objp->type != OBJ_SHIP ){
3344 // make an orientation matrix from the o_fvec
3345 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3347 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3348 // hack, but should be suitable.
3349 shipp = &Ships[objp->instance];
3350 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3354 wid = ssp->system_info->turret_weapon_type;
3356 // bash the position and orientation of the turret
3357 ssp->submodel_info_1.angs.h = (float)heading;
3358 ssp->submodel_info_2.angs.p = (float)pitch;
3360 // get the world position of the weapon
3361 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3363 // create the weapon object
3364 if(wnet_signature != 0){
3365 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3367 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3368 if (weapon_objnum != -1) {
3369 if ( Weapon_info[wid].launch_snd != -1 ) {
3370 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3375 // send a mission log item packet
3376 void send_mission_log_packet( int num )
3379 ubyte data[MAX_PACKET_SIZE];
3384 Assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3386 // get the data from the log
3387 entry = &log_entries[num];
3388 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3389 sindex = (ushort)entry->index;
3391 BUILD_HEADER(MISSION_LOG_ENTRY);
3393 ADD_DATA_S32(entry->flags);
3394 ADD_DATA_U16(sindex);
3395 ADD_DATA(entry->timestamp);
3396 ADD_STRING(entry->pname);
3397 ADD_STRING(entry->sname);
3399 // broadcast the packet to all players
3400 multi_io_send_to_all_reliable(data, packet_size);
3403 // process a mission log item packet
3404 void process_mission_log_packet( ubyte *data, header *hinfo )
3409 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3412 Assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3414 offset = HEADER_LENGTH;
3416 GET_DATA_S32(flags);
3417 GET_DATA_U16(sindex);
3418 GET_DATA(timestamp);
3424 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3427 // send a mission message packet
3428 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)
3431 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3433 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3434 Assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3435 Assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3437 up = (ubyte) priority;
3438 us = (ubyte) source;
3439 utime = (ubyte)timing;
3441 BUILD_HEADER(MISSION_MESSAGE);
3443 ADD_STRING(who_from);
3447 ADD_DATA_S32(builtin_type);
3448 ADD_DATA_S32(multi_team_filter);
3450 if (multi_target == -1){
3451 multi_io_send_to_all_reliable(data, packet_size);
3453 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3457 // process a mission message packet
3458 void process_mission_message_packet( ubyte *data, header *hinfo )
3460 int offset, id, builtin_type;
3461 ubyte priority, source, utiming;
3462 char who_from[NAME_LENGTH];
3463 int multi_team_filter;
3465 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3467 offset = HEADER_LENGTH;
3469 GET_STRING(who_from);
3473 GET_DATA_S32(builtin_type);
3474 GET_DATA_S32(multi_team_filter);
3478 // filter out builtin ones in TvT
3479 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3483 // maybe filter this out
3484 if(!message_filter_multi(id)){
3485 // send the message as if it came from an sexpression
3486 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3490 // just send them a pong back as fast as possible
3491 void process_ping_packet(ubyte *data, header *hinfo)
3496 offset = HEADER_LENGTH;
3499 // get the address to return the pong to
3500 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3506 // right now it just routes the pong through to the standalone gui, which is the only
3507 // system which uses ping and pong right now.
3508 void process_pong_packet(ubyte *data, header *hinfo)
3514 offset = HEADER_LENGTH;
3516 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3520 // if we're connected , see who sent us this pong
3521 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3522 lookup = find_player_id(hinfo->id);
3527 p = &Net_players[lookup];
3529 // evaluate the ping
3530 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3532 // put in calls to any functions which may want to know about the ping times from
3534 if(Game_mode & GM_STANDALONE_SERVER){
3535 std_update_player_ping(p);
3538 // mark his socket as still alive (extra precaution)
3539 psnet_mark_received(Net_players[lookup].reliable_socket);
3541 // otherwise, do any special processing
3543 // if we're in the join game state, see if this pong came from a server on our
3545 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3546 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3551 // send a ping packet
3552 void send_ping(net_addr *addr)
3554 unsigned char data[8];
3557 // build the header and send the packet
3558 BUILD_HEADER( PING );
3559 psnet_send(addr, &data[0], packet_size);
3562 // send a pong packet
3563 void send_pong(net_addr *addr)
3565 unsigned char data[8];
3568 // build the header and send the packet
3570 psnet_send(addr, &data[0], packet_size);
3573 // sent from host to master. give me the list of missions you have.
3574 // this will be used only in a standalone mode
3575 void send_mission_list_request( int what )
3577 ubyte data[MAX_PACKET_SIZE];
3580 // build the header and ask for a list of missions or campaigns (depending
3581 // on the 'what' flag).
3582 BUILD_HEADER(MISSION_REQUEST);
3584 multi_io_send_reliable(Net_player, data, packet_size);
3587 // maximum number of bytes that we can send in a mission items packet.
3588 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3590 // defines used to tell what type of packets are being sent
3591 #define MISSION_LIST_ITEMS 1
3592 #define CAMPAIGN_LIST_ITEMS 2
3594 // send an individual mission file item
3595 void send_mission_items( net_player *pl )
3597 ubyte data[MAX_PACKET_SIZE];
3602 BUILD_HEADER(MISSION_ITEM);
3604 // send the list of missions and campaigns avilable on the server. Stop when
3605 // reaching a certain maximum
3606 type = MISSION_LIST_ITEMS;
3608 for (i = 0; i < Multi_create_mission_count; i++ ) {
3612 ADD_STRING( Multi_create_mission_list[i].filename );
3613 ADD_STRING( Multi_create_mission_list[i].name );
3614 ADD_DATA_S32( Multi_create_mission_list[i].flags );
3615 ADD_DATA( Multi_create_mission_list[i].max_players );
3616 ADD_DATA_U32( Multi_create_mission_list[i].respawn );
3619 ADD_DATA( Multi_create_mission_list[i].valid_status );
3621 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3624 multi_io_send_reliable(pl, data, packet_size);
3625 BUILD_HEADER( MISSION_ITEM );
3631 multi_io_send_reliable(pl, data, packet_size);
3633 // send the campaign information
3634 type = CAMPAIGN_LIST_ITEMS;
3635 BUILD_HEADER(MISSION_ITEM);
3637 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3641 ADD_STRING( Multi_create_campaign_list[i].filename );
3642 ADD_STRING( Multi_create_campaign_list[i].name );
3643 ADD_DATA_S32( Multi_create_campaign_list[i].flags );
3644 ADD_DATA( Multi_create_campaign_list[i].max_players );
3646 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3649 multi_io_send_reliable(pl, data, packet_size);
3650 BUILD_HEADER( MISSION_ITEM );
3656 multi_io_send_reliable(pl, data, packet_size);
3659 // process a request for a list of missions
3660 void process_mission_request_packet(ubyte *data, header *hinfo)
3662 int player_num,offset;
3664 offset = HEADER_LENGTH;
3667 // fill in the address information of where this came from
3668 player_num = find_player_id(hinfo->id);
3669 if(player_num == -1){
3670 nprintf(("Network","Could not find player to send mission list items to!\n"));
3674 send_mission_items( &Net_players[player_num] );
3677 // process an individual mission file item
3678 void process_mission_item_packet(ubyte *data,header *hinfo)
3681 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3682 ubyte stop, type,max_players;
3685 Assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3686 offset = HEADER_LENGTH;
3691 GET_STRING( filename );
3693 GET_DATA_S32( flags );
3694 GET_DATA( max_players );
3696 // missions also have respawns and a crc32 associated with them
3697 if(type == MISSION_LIST_ITEMS){
3698 GET_DATA_U32(respawn);
3701 GET_DATA(valid_status);
3703 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3704 strcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename );
3705 strcpy(Multi_create_mission_list[Multi_create_mission_count].name, name );
3706 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3707 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3708 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3711 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3713 Multi_create_mission_count++;
3715 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3716 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3717 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename );
3718 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name );
3719 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3720 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3721 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3722 Multi_create_campaign_count++;
3731 // this will cause whatever list to get resorted (although they should be appearing in order)
3732 multi_create_setup_list_data(-1);
3735 // send a request to the server to pause or unpause the game
3736 void send_multi_pause_packet(int pause)
3738 ubyte data[MAX_PACKET_SIZE];
3740 int packet_size = 0;
3742 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3745 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3746 val = (ubyte) pause;
3748 // add the pause info
3751 // send the request to the server
3752 multi_io_send_reliable(Net_player, data, packet_size);
3755 // process a pause update packet (pause, unpause, etc)
3756 void process_multi_pause_packet(ubyte *data, header *hinfo)
3762 offset = HEADER_LENGTH;
3768 // get who sent the packet
3769 player_index = find_player_id(hinfo->id);
3770 // if we don't know who sent the packet, don't do anything
3771 if(player_index == -1){
3775 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3776 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3779 // send a game information update
3780 void send_game_info_packet()
3783 ubyte data[MAX_PACKET_SIZE], paused;
3785 // set the paused variable
3786 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3788 BUILD_HEADER(GAME_INFO);
3789 ADD_DATA_S32( Missiontime );
3792 multi_io_send_to_all(data, packet_size);
3795 // process a game information update
3796 void process_game_info_packet( ubyte *data, header *hinfo )
3802 offset = HEADER_LENGTH;
3804 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3805 // time, set our time to server time (should take ping time into account!!!)
3806 GET_DATA( mission_time );
3811 // send an ingame nak packet
3812 void send_ingame_nak(int state, net_player *p)
3814 ubyte data[MAX_PACKET_SIZE];
3817 BUILD_HEADER(INGAME_NAK);
3819 ADD_DATA_S32(state);
3821 multi_io_send_reliable(p, data, packet_size);
3824 // process an ingame nak packet
3825 void process_ingame_nak(ubyte *data, header *hinfo)
3827 int offset,state,pid;
3830 offset = HEADER_LENGTH;
3831 GET_DATA_S32(state);
3834 pid = find_player_id(hinfo->id);
3838 pl = &Net_players[pid];
3841 case ACK_FILE_ACCEPTED :
3842 Assert(Net_player->flags & NETINFO_FLAG_INGAME_JOIN);
3843 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3844 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3849 // send a packet telling players to end the mission
3850 void send_endgame_packet(net_player *pl)
3852 ubyte data[MAX_PACKET_SIZE];
3856 BUILD_HEADER(MISSION_END);
3858 // sending to a specific player?
3860 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3861 multi_io_send_reliable(pl, data, packet_size);
3865 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3866 // send all player stats here
3867 multi_broadcast_stats(STATS_MISSION);
3869 // if in dogfight mode, send all dogfight stats as well
3870 ml_string("Before dogfight stats!");
3871 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3872 ml_string("Sending dogfight stats!");
3874 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3876 ml_string("After dogfight stats!");
3878 // tell everyone to leave the game
3879 multi_io_send_to_all_reliable(data, packet_size);
3881 multi_io_send_reliable(Net_player, data, packet_size);
3885 // process a packet indicating we should end the current mission
3886 void process_endgame_packet(ubyte *data, header *hinfo)
3891 offset = HEADER_LENGTH;
3895 ml_string("Receiving endgame packet");
3897 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3898 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3899 // determine who this came from and make sure he is allowed to end the game
3900 player_num = find_player_id(hinfo->id);
3901 Assert(player_num != -1);
3906 // if the player is allowed to end the mission
3907 if(!multi_can_end_mission(&Net_players[player_num])){
3911 // act as if we hit alt+j locally
3912 multi_handle_end_mission_request();
3914 // all clients process immediately
3916 // ingame joiners should quit when they receive an endgame packet since the game is over
3917 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3918 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3922 // do any special processing for being in a state other than the gameplay state
3923 multi_handle_state_special();
3925 // make sure we're not already in the debrief state
3926 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3927 multi_warpout_all_players();
3932 // send a position/orientation update for myself (if I'm an observer)
3933 void send_observer_update_packet()
3935 ubyte data[MAX_PACKET_SIZE];
3940 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3941 // to send any update packets to anyone.
3942 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3946 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3952 BUILD_HEADER(OBSERVER_UPDATE);
3954 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3956 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3959 // add targeting infomation
3960 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3961 target_sig = Objects[Player_ai->target_objnum].net_signature;
3965 ADD_DATA_U16(target_sig);
3967 multi_io_send(Net_player, data, packet_size);
3970 // process a position/orientation update from an observer
3971 void process_observer_update_packet(ubyte *data, header *hinfo)
3977 physics_info bogus_pi;
3980 offset = HEADER_LENGTH;
3982 obs_num = find_player_id(hinfo->id);
3984 memset(&bogus_pi,0,sizeof(physics_info));
3985 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
3987 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
3990 // targeting information
3991 GET_DATA_U16(target_sig);
3994 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
3998 // set targeting info
3999 if(target_sig == 0){
4000 Net_players[obs_num].s_info.target_objnum = -1;
4002 target_obj = multi_get_network_object(target_sig);
4003 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
4006 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
4007 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
4008 Net_players[obs_num].s_info.eye_pos = g_vec;
4009 Net_players[obs_num].s_info.eye_orient = g_mat;
4012 void send_netplayer_slot_packet()
4014 ubyte data[MAX_PACKET_SIZE];
4015 int packet_size,idx;
4020 BUILD_HEADER(NETPLAYER_SLOTS_P);
4021 for(idx=0;idx<MAX_PLAYERS;idx++){
4022 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
4024 ADD_DATA_S16(Net_players[idx].player_id);
4025 ADD_DATA_U16(Objects[Net_players[idx].player->objnum].net_signature);
4026 ADD_DATA_S32(Net_players[idx].p_info.ship_class);
4027 ADD_DATA_S32(Net_players[idx].p_info.ship_index);
4033 // standalone case or not
4034 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4035 multi_io_send_to_all_reliable(data, packet_size);
4037 multi_io_send_reliable(Net_player, data, packet_size);
4041 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
4044 int player_num,ship_class,ship_index;
4050 offset = HEADER_LENGTH;
4052 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
4053 multi_untag_player_ships();
4057 GET_DATA_S16(player_id);
4058 GET_DATA_U16(net_sig);
4059 GET_DATA_S32(ship_class);
4060 GET_DATA_S32(ship_index);
4061 player_num = find_player_id(player_id);
4063 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
4065 // call the function in multiutil.cpp to set up the player object stuff
4066 // being careful not to muck with the standalone object
4067 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
4068 objp = multi_get_network_object(net_sig);
4069 Assert(objp != NULL);
4070 multi_assign_player_ship( player_num, objp, ship_class );
4071 Net_players[player_num].p_info.ship_index = ship_index;
4072 objp->flags &= ~(OF_COULD_BE_PLAYER);
4073 objp->flags |= OF_PLAYER_SHIP;
4080 // standalone should forward the packet and wait for a response
4081 if(Game_mode & GM_STANDALONE_SERVER){
4082 send_netplayer_slot_packet();
4085 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
4086 send_netplayer_update_packet();
4089 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
4090 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
4091 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
4092 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
4094 ubyte data[MAX_PACKET_SIZE], utmp;
4097 BUILD_HEADER(SHIP_WSTATE_CHANGE);
4098 ADD_DATA_U16( Objects[shipp->objnum].net_signature );
4099 utmp = (ubyte)(what);
4101 utmp = (ubyte)(new_bank);
4103 utmp = (ubyte)(link_status);
4106 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
4107 multi_io_send_to_all_reliable(data, packet_size);
4110 void process_ship_weapon_change( ubyte *data, header *hinfo )
4114 ubyte what, new_bank, link_status;
4118 offset = HEADER_LENGTH;
4119 GET_DATA_U16( signature );
4121 GET_DATA( new_bank );
4122 GET_DATA( link_status );
4125 objp = multi_get_network_object( signature );
4126 if ( objp == NULL ) {
4127 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4130 // Assert( objp->type == OBJ_SHIP );
4131 if(objp->type != OBJ_SHIP){
4135 // if this is my data, do nothing since I already have my own data
4136 if ( objp == Player_obj ){
4140 // now, get the ship and set the new bank and link modes based on the 'what' value
4141 shipp = &Ships[objp->instance];
4142 if ( what == MULTI_PRIMARY_CHANGED ) {
4143 shipp->weapons.current_primary_bank = new_bank;
4145 shipp->flags |= SF_PRIMARY_LINKED;
4147 shipp->flags &= ~SF_PRIMARY_LINKED;
4150 shipp->weapons.current_secondary_bank = new_bank;
4152 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4154 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4159 // ship status change procedure
4160 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4162 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4163 // for added redundancy.
4164 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4165 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4166 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4167 // back to the client. Also sends multiple times for redundancy
4168 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4169 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4170 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4172 #define SHIP_STATUS_REPEAT 2
4173 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4176 ubyte data[MAX_PACKET_SIZE];
4177 int packet_size = 0;
4183 BUILD_HEADER(SHIP_STATUS_CHANGE);
4185 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4186 temp = bi->status[idx];
4190 // server should send reliably (response packet)
4191 if(MULTIPLAYER_MASTER){
4192 multi_io_send_reliable(pl, data, packet_size);
4194 multi_io_send(pl, data, packet_size);
4198 void process_ship_status_packet(ubyte *data, header *hinfo)
4202 int player_num,unique_id;
4206 offset = HEADER_LENGTH;
4208 // zero out the button info structure for good measure
4209 memset(&bi,0,sizeof(button_info));
4211 // read the button-info
4212 GET_DATA_S32(unique_id);
4214 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4215 GET_DATA_S32(i_tmp);
4216 bi.status[idx] = i_tmp;
4221 // this will be handled differently client and server side. Duh.
4222 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4223 // find which net-player has sent us butotn information
4224 player_num = find_player_id(hinfo->id);
4225 Assert(player_num >= 0);
4230 // don't process critical button information for observers
4231 // its a new button_info for this guy. apply and ack
4232 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4233 // mark that he's pressed this button
4234 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4236 // send a return packet
4237 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4239 // apply the button presses to his ship as normal
4240 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4242 // else ignore it as a repeat from the same guy
4243 } else { // CLIENT SIDE
4244 // this is the return from the server, so we should now apply them locally
4245 // if(lookup_ship_status(Net_player,unique_id,1)){
4246 multi_apply_ship_status(Net_player, &bi, 1);
4251 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4252 // for clients. Left all details to this function instead of higher level messaging
4254 void send_player_order_packet(int type, int index, int cmd)
4256 ubyte data[MAX_PACKET_SIZE];
4258 ushort target_signature;
4260 int packet_size = 0;
4262 BUILD_HEADER(PLAYER_ORDER_PACKET);
4265 ADD_DATA(val); // ship order or wing order, or message all fighters
4267 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4268 if ( val != SQUAD_MSG_ALL ){
4269 ADD_DATA_S32(index); // net signature of target ship
4272 ADD_DATA_S32(cmd); // the command itself
4275 target_signature = 0;
4276 if ( Player_ai->target_objnum != -1 ){
4277 target_signature = Objects[Player_ai->target_objnum].net_signature;
4280 ADD_DATA_U16( target_signature );
4283 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4286 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4287 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
4288 t_subsys = (char)s_index;
4292 multi_io_send_reliable(Net_player, data, packet_size);
4295 // brief explanation :
4296 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4297 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4298 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4299 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4300 void process_player_order_packet(ubyte *data, header *hinfo)
4302 int offset, player_num, command, index = 0, tobjnum_save;
4303 ushort target_signature;
4304 char t_subsys, type;
4305 object *objp, *target_objp;
4308 ship_subsys *tsubsys_save, *targeted_subsys;
4310 Assert(MULTIPLAYER_MASTER);
4312 // packet values - its easier to read all of these in first
4314 offset = HEADER_LENGTH;
4317 if ( type != SQUAD_MSG_ALL ){
4318 GET_DATA_S32( index );
4321 GET_DATA_S32( command );
4322 GET_DATA_U16( target_signature );
4323 GET_DATA( t_subsys );
4327 player_num = find_player_id(hinfo->id);
4328 if(player_num == -1){
4329 nprintf(("Network","Received player order packet from unknown player\n"));
4333 objp = &Objects[Net_players[player_num].player->objnum];
4334 if ( objp->type != OBJ_SHIP ) {
4335 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4339 // HACK HACK HACK HACK HACK HACK
4340 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4341 // the data is BOGUS. All people should be able to to these things as well.
4342 if(command == REARM_REPAIR_ME_ITEM){
4343 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4345 } else if(command == ABORT_REARM_REPAIR_ITEM){
4346 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4350 // if this player is not allowed to do messaging, quit here
4351 if( !multi_can_message(&Net_players[player_num]) ){
4352 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4356 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4357 // then call them in.
4358 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4359 Assert( (index >= 0) && (index < Num_reinforcements) );
4360 hud_squadmsg_call_reinforcement(index, player_num);
4364 // set the player's ai information here
4365 shipp = &Ships[objp->instance];
4366 aip = &Ai_info[shipp->ai_index];
4368 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4369 target_objp = multi_get_network_object( target_signature );
4370 if ( target_objp == NULL ) {
4374 targeted_subsys = NULL;
4375 if ( t_subsys != -1 ) {
4376 Assert( target_objp != NULL );
4377 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4380 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4382 tobjnum_save = aip->target_objnum;
4383 tsubsys_save = aip->targeted_subsys;
4385 if ( target_objp ) {
4386 aip->target_objnum = OBJ_INDEX(target_objp);
4388 aip->target_objnum = -1;
4391 aip->targeted_subsys = targeted_subsys;
4393 if ( type == SQUAD_MSG_SHIP ) {
4394 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4395 } else if ( type == SQUAD_MSG_WING ) {
4396 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4397 } else if ( type == SQUAD_MSG_ALL ) {
4398 hud_squadmsg_send_to_all_fighters( command, player_num );
4401 Assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4402 aip->target_objnum = tobjnum_save;
4403 aip->targeted_subsys = tsubsys_save;
4406 // FILE SIGNATURE stuff :
4407 // there are 2 cases for file signature sending which are handled very differently
4408 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4409 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4410 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4411 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4412 // would have to keep track of up to potentially 14 other ack handles (ouch).
4413 void send_file_sig_packet(ushort sum_sig,int length_sig)
4415 ubyte data[MAX_PACKET_SIZE];
4416 int packet_size = 0;
4418 BUILD_HEADER(FILE_SIG_INFO);
4419 ADD_DATA_U16(sum_sig);
4420 ADD_DATA_S16(length_sig);
4422 multi_io_send_reliable(Net_player, data, packet_size);
4425 void process_file_sig_packet(ubyte *data, header *hinfo)
4430 offset = HEADER_LENGTH;
4432 // should only be received on the server-side
4433 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4435 GET_DATA_U16(sum_sig);
4436 GET_DATA_S32(length_sig);
4438 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4441 void send_file_sig_request(char *file_name)
4443 ubyte data[MAX_PACKET_SIZE];
4444 int packet_size = 0;
4446 BUILD_HEADER(FILE_SIG_REQUEST);
4447 ADD_STRING(file_name);
4449 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4451 multi_io_send_to_all_reliable(data, packet_size);
4454 void process_file_sig_request(ubyte *data, header *hinfo)
4456 int offset = HEADER_LENGTH;
4458 // get the mission name
4459 GET_STRING(Netgame.mission_name);
4462 // set the current mission filename
4463 strcpy(Game_current_mission_filename,Netgame.mission_name);
4466 multi_get_mission_checksum(Game_current_mission_filename);
4468 if(!multi_endgame_ending()){
4469 // reply to the server
4470 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4474 // functions to deal with subsystems getting whacked
4475 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4477 ubyte data[MAX_PACKET_SIZE];
4480 vector tmp, local_hitpos;
4483 Assert ( index < UCHAR_MAX );
4484 uindex = (ubyte)(index);
4486 objp = &Objects[shipp->objnum];
4488 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4489 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4491 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4492 ADD_DATA_U16( Objects[shipp->objnum].net_signature );
4494 // ADD_DATA( local_hitpos );
4495 add_vector_data(data, &packet_size, local_hitpos);
4497 multi_io_send_to_all_reliable(data, packet_size);
4500 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4506 vector local_hit_pos, world_hit_pos;
4508 offset = HEADER_LENGTH;
4510 GET_DATA_U16( signature );
4512 // GET_DATA( local_hit_pos );
4513 get_vector_data(data, &offset, local_hit_pos);
4515 // get the network object. process it if we find it.
4516 objp = multi_get_network_object( signature );
4517 if ( objp != NULL ) {
4519 ship_subsys *subsysp;
4521 // be sure we have a ship!!!
4522 // Assert ( objp->type == OBJ_SHIP );
4523 if(objp->type != OBJ_SHIP){
4528 shipp = &Ships[objp->instance];
4530 // call to get the pointer to the subsystem we should be working on
4531 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4532 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4533 vm_vec_add2( &world_hit_pos, &objp->pos );
4535 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4536 if ( objp == Player_obj ) {
4537 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4545 // packet to tell clients cargo of a ship was revealed to all
4546 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4548 ubyte data[MAX_PACKET_SIZE], uindex;
4551 Assert ( index < UCHAR_MAX );
4552 uindex = (ubyte)(index);
4554 // build the header and add the data
4555 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4556 ADD_DATA_U16( Objects[shipp->objnum].net_signature );
4559 // server sends to all players
4560 if(MULTIPLAYER_MASTER){
4561 multi_io_send_to_all_reliable(data, packet_size);
4563 // clients just send to the server
4565 multi_io_send_reliable(Net_player, data, packet_size);
4569 // process a subsystem cargo revealed packet
4570 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4577 ship_subsys *subsysp;
4579 offset = HEADER_LENGTH;
4580 GET_DATA_U16( signature );
4584 // get a ship pointer and call the ship function to reveal the cargo
4585 objp = multi_get_network_object( signature );
4586 if ( objp == NULL ) {
4587 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4591 // Assert( objp->type == OBJ_SHIP );
4592 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4596 shipp = &Ships[objp->instance];
4598 // call to get the pointer to the subsystem we should be working on
4599 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4600 if (subsysp == NULL) {
4601 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4605 // this will take care of re-routing to all other clients
4606 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4607 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4609 // server should rebroadcast
4610 if(MULTIPLAYER_MASTER){
4611 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4615 void send_netplayer_load_packet(net_player *pl)
4617 ubyte data[MAX_PACKET_SIZE];
4618 int packet_size = 0;
4620 BUILD_HEADER(LOAD_MISSION_NOW);
4621 ADD_STRING(Netgame.mission_name);
4624 multi_io_send_to_all_reliable(data, packet_size);
4626 multi_io_send_reliable(pl, data, packet_size);
4630 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4633 int offset = HEADER_LENGTH;
4638 strcpy(Netgame.mission_name,str);
4639 strcpy(Game_current_mission_filename,str);
4640 if(!Multi_mission_loaded){
4642 // MWA 2/3/98 -- ingame join changes!!!
4643 // everyone can go through the same mission loading path here!!!!
4644 nprintf(("Network","Loading mission..."));
4646 // notify everyone that I'm loading the mission
4647 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4648 send_netplayer_update_packet();
4650 // do the load itself
4651 game_start_mission();
4653 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4654 // code will remark the correct player ships
4655 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4656 multi_untag_player_ships();
4659 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4660 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4661 send_netplayer_update_packet();
4663 Multi_mission_loaded = 1;
4664 nprintf(("Network","Finished loading mission\n"));
4668 void send_jump_into_mission_packet(net_player *pl)
4670 ubyte data[MAX_PACKET_SIZE];
4671 int packet_size = 0;
4673 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4675 BUILD_HEADER(JUMP_INTO_GAME);
4677 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4678 // and possible other things.
4680 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4681 ADD_DATA_S32(Netgame.game_state);
4687 multi_io_send_to_all_reliable(data, packet_size);
4689 // send to a specific player
4691 multi_io_send_reliable(pl, data, packet_size);
4695 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4697 int offset = HEADER_LENGTH;
4702 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4703 // the game could be paused, so ingame joiner needs to deal with it.
4704 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4705 GET_DATA_S32( state );
4706 Netgame.game_state = state;
4711 // handle any special processing for being in a weird substate
4712 multi_handle_state_special();
4714 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4715 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4716 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4717 multi_ingame_observer_finish();
4719 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4720 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4721 send_netplayer_update_packet();
4724 // start the mission!!
4725 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4726 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4727 gameseq_post_event(GS_EVENT_ENTER_GAME);
4728 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4729 send_netplayer_update_packet();
4733 extern int Player_multi_died_check;
4734 Player_multi_died_check = -1;
4736 // recalc all object pairs now
4737 extern void obj_reset_all_collisions();
4738 obj_reset_all_collisions();
4740 // display some cool text
4741 multi_common_add_text(XSTR("Received mission start\n",720),1);
4744 ml_string(NOX("Client received mission start from server - entering mission"));
4749 char *repair_text[] = {
4751 "REPAIR_INFO_BEGIN",
4753 "REPAIR_INFO_UPDATE",
4754 "REPAIR_INFO_QUEUE",
4755 "REPAIR_INFO_ABORT",
4756 "REPAIR_INFO_BROKEN",
4757 "REPAIR_INFO_WARP_ADD",
4758 "REPAIR_INFO_WARP_REMOVE",
4759 "REPAIR_INFO_ONWAY",
4760 "REPAIR_INFO_KILLED",
4761 "REPAIR_INFO_COMPLETE",
4766 // the following two routines deal with updating and sending information regarding players
4767 // rearming and repairing during the game. The process function calls the routines to deal with
4768 // setting flags and other interesting things.
4769 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4771 int packet_size = 0;
4772 ushort repaired_signature, repair_signature;
4773 ubyte data[MAX_PACKET_SIZE];
4776 // use the network signature of the destination object if there is one, -1 otherwise.
4777 // client will piece it all together
4778 repaired_signature = repaired_objp->net_signature;
4780 // the repair ship may be NULL here since it might have been destroyed
4781 repair_signature = 0;
4783 repair_signature = repair_objp->net_signature;
4786 BUILD_HEADER(CLIENT_REPAIR_INFO);
4789 ADD_DATA_U16( repaired_signature );
4790 ADD_DATA_U16( repair_signature );
4792 multi_io_send_to_all_reliable(data, packet_size);
4794 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));
4797 void process_repair_info_packet(ubyte *data, header *hinfo)
4799 int offset = HEADER_LENGTH;
4800 ushort repaired_signature, repair_signature;
4801 object *repaired_objp, *repair_objp;
4805 GET_DATA_U16( repaired_signature );
4806 GET_DATA_U16( repair_signature );
4809 repaired_objp = multi_get_network_object( repaired_signature );
4810 repair_objp = multi_get_network_object( repair_signature );
4812 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));
4814 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4818 if ( repaired_objp == NULL ) {
4819 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4823 // the hope is to simply call the routine in the ai code to set/unset flags
4824 // based on the code value and everything else should happen..I hope....
4825 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4827 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4829 // set the dock flags when repair begins. Prevents problem in lagging docking
4830 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4832 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4833 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4834 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4837 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4839 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4840 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4843 if ( code == REPAIR_INFO_WARP_ADD ){
4844 mission_warp_in_support_ship( repaired_objp );
4846 mission_remove_scheduled_repair( repaired_objp );
4851 // sends information updating clients on certain AI information that clients will
4852 // need to know about to keep HUD information up to date. objp is the object that we
4853 // are updating, and what is the type of stuff that we are updating.
4854 void send_ai_info_update_packet( object *objp, char what )
4857 ushort other_signature;
4858 ubyte data[MAX_PACKET_SIZE];
4860 ubyte dock_index, dockee_index;
4862 // Assert( objp->type == OBJ_SHIP );
4863 if(objp->type != OBJ_SHIP){
4866 aip = &Ai_info[Ships[objp->instance].ai_index];
4869 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4872 BUILD_HEADER( AI_INFO_UPDATE );
4873 ADD_DATA_U16( objp->net_signature );
4876 // depending on the "what" value, we will send different information
4880 case AI_UPDATE_DOCK:
4881 // for docking ships, add the signature of the ship that we are docking with.
4882 Assert( aip->dock_objnum != -1 );
4883 other_signature = Objects[aip->dock_objnum].net_signature;
4884 dock_index = (ubyte)(aip->dock_index);
4885 dockee_index = (ubyte)(aip->dockee_index);
4886 ADD_DATA_U16( other_signature );
4887 ADD_DATA(dock_index);
4888 ADD_DATA(dockee_index);
4891 case AI_UPDATE_UNDOCK:
4892 // for undocking ships, check the dock_objnum since we might or might not have it
4893 // depending on whether or not a ship was destroyed while we were docked.
4894 other_signature = 0;
4895 if ( aip->dock_objnum != -1 )
4896 other_signature = Objects[aip->dock_objnum].net_signature;
4897 ADD_DATA_U16( other_signature );
4901 case AI_UPDATE_ORDERS: {
4904 // for orders, we only need to send a little bit of information here. Be sure that the
4905 // first order for this ship is active
4906 Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4907 ADD_DATA_S32( aip->goals[0].ai_mode );
4908 ADD_DATA_S32( aip->goals[0].ai_submode );
4910 if ( aip->goals[0].ship_name != NULL )
4911 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4913 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4914 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4915 if ( shipnum != -1 ) {
4916 Assert( Ships[shipnum].objnum != -1 );
4917 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4919 other_signature = 0;
4921 ADD_DATA_U16( other_signature );
4923 // for docking, add the dock and dockee index
4924 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4925 Assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4926 Assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4927 dock_index = (ubyte)aip->goals[0].docker.index;
4928 dockee_index = (ubyte)aip->goals[0].dockee.index;
4929 ADD_DATA( dock_index );
4930 ADD_DATA( dockee_index );
4939 multi_rate_add(1, "aiu", packet_size);
4940 multi_io_send_to_all_reliable(data, packet_size);
4943 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4944 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4945 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4947 int offset = HEADER_LENGTH;
4949 ushort net_signature, other_net_signature;
4950 object *objp, *other_objp;
4953 ubyte dock_index = 0, dockee_index = 0;
4955 GET_DATA_U16( net_signature ); // signature of the object that we are dealing with.
4956 GET_DATA( code ); // code of what we are doing.
4957 objp = multi_get_network_object( net_signature );
4959 nprintf(("Network", "Couldn't find object for ai update\n"));
4962 case AI_UPDATE_DOCK:
4963 GET_DATA_U16( other_net_signature );
4964 GET_DATA( dock_index );
4965 GET_DATA( dockee_index );
4966 other_objp = multi_get_network_object( other_net_signature );
4968 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
4970 // if we don't have an object to work with, break out of loop
4971 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
4975 Assert( other_objp->type == OBJ_SHIP );
4976 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
4977 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
4979 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4980 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4982 ai_do_objects_docked_stuff( objp, other_objp );
4985 case AI_UPDATE_UNDOCK:
4986 GET_DATA_U16( other_net_signature );
4987 other_objp = multi_get_network_object( other_net_signature );
4989 // if we don't have an object to work with, break out of loop
4993 ai_do_objects_undocked_stuff( objp, other_objp );
4996 case AI_UPDATE_ORDERS:
4997 GET_DATA_S32( mode );
4998 GET_DATA_S32( submode );
4999 GET_DATA_U16( other_net_signature );
5000 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5001 GET_DATA(dock_index);
5002 GET_DATA(dockee_index);
5005 // be sure that we have a ship object!!!
5006 if ( !objp || (objp->type != OBJ_SHIP) )
5009 // set up the information in the first goal element of the object in question
5010 aip = &Ai_info[Ships[objp->instance].ai_index];
5011 aip->active_goal = 0;
5012 aip->goals[0].ai_mode = mode;
5013 aip->goals[0].ai_submode = submode;
5015 // for docking, add the dock and dockee index
5016 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5017 aip->dock_index = dock_index;
5018 aip->dockee_index = dockee_index;
5021 // get a shipname if we can.
5022 other_objp = multi_get_network_object( other_net_signature );
5023 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
5024 // get a pointer to the shipname in question. Use the ship_name value in the
5025 // ship. We are only using this for HUD display, so I think that using this
5026 // method will be fine.
5027 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
5029 // special case for destroy subsystem -- get the ai_info pointer to our target ship
5030 // so that we can properly set up what subsystem this ship is attacking.
5031 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
5032 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
5034 // if docking -- set the dock index and dockee index of this other ship
5035 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5036 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5037 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5044 Int3(); // this Int3() should be temporary
5045 nprintf(("Network", "Invalid code for ai update: %d\n", code));
5051 // tell the standalone to move into the MISSION_SYNC_STATE
5052 void send_mission_sync_packet(int mode,int start_campaign)
5054 ubyte data[MAX_PACKET_SIZE],is_campaign;
5055 int packet_size = 0;
5057 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
5059 // build the header and add the sync mode (pre or post briefing)
5060 BUILD_HEADER(MISSION_SYNC_DATA);
5063 // if this is a campaign game
5064 if(mode == MULTI_SYNC_PRE_BRIEFING){
5065 if(Game_mode & GM_CAMPAIGN_MODE){
5066 // add a byte indicating campaign mode
5068 ADD_DATA(is_campaign);
5070 // add a byte indicating if we should be starting a campaign or continuing it
5071 is_campaign = (ubyte)start_campaign;
5072 ADD_DATA(is_campaign);
5074 // add the campaign filename
5075 ADD_STRING(Netgame.campaign_name);
5077 // otherwise if this is a single mission
5079 // add a byte indicating single mission mode
5081 ADD_DATA(is_campaign);
5083 // add the mission filename
5084 ADD_STRING(Game_current_mission_filename);
5087 multi_io_send_reliable(Net_player, data, packet_size);
5090 // move into the MISSION_SYNC state when this is received
5091 // this packet is sent only from a game host to a standalone
5092 void process_mission_sync_packet(ubyte *data, header *hinfo)
5095 ubyte campaign_flag;
5096 int offset = HEADER_LENGTH;
5098 Assert(Game_mode & GM_STANDALONE_SERVER);
5100 // if this is a team vs team situation, lock the players send a final team update
5101 if(Netgame.type_flags & NG_TYPE_TEAM){
5102 multi_team_host_lock_all();
5103 multi_team_send_update();
5106 // get the sync mode (pre or post briefing)
5109 if(mode == MULTI_SYNC_PRE_BRIEFING){
5110 // get the flag indicating if this is a single mission or a campaign mode
5111 GET_DATA(campaign_flag);
5113 // get the flag indicating whether we should be starting a new campaign
5114 GET_DATA(campaign_flag);
5116 // get the campaign filename
5117 GET_STRING(Netgame.campaign_name);
5119 // either start a new campaign or continue on to the next mission in the current campaign
5121 multi_campaign_start(Netgame.campaign_name);
5123 multi_campaign_next_mission();
5126 // make sure we remove the campaign mode flag
5127 Game_mode &= ~(GM_CAMPAIGN_MODE);
5129 // get the single mission filename
5130 GET_STRING(Game_current_mission_filename);
5131 strcpy(Netgame.mission_name,Game_current_mission_filename);
5136 // set the correct mode and m ove into the state
5137 Multi_sync_mode = mode;
5138 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5141 // tell a player to merge his mission stats into his alltime stats
5142 void send_store_stats_packet(int accept)
5145 int packet_size = 0;
5147 BUILD_HEADER(STORE_MISSION_STATS);
5149 // add whether we're accepting or tossing
5150 val = (ubyte)accept;
5153 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5154 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5155 multi_io_send_to_all_reliable(data, packet_size);
5157 multi_io_send_reliable(Net_player, data, packet_size);
5161 void process_store_stats_packet(ubyte *data, header *hinfo)
5163 int offset = HEADER_LENGTH;
5169 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5170 if(Game_mode & GM_STANDALONE_SERVER){
5171 // rebroadcast the packet to all others in the game
5172 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5173 multi_io_send_to_all_reliable(data, offset);
5176 // all players should mark the stats as being accepted in the debriefing
5177 multi_debrief_stats_accept();
5179 // all players should mark the stats as being "tossed" in the debriefing
5180 multi_debrief_stats_toss();
5185 void send_debris_update_packet(object *objp,int code)
5187 ubyte data[MAX_PACKET_SIZE];
5189 int packet_size = 0;
5191 BUILD_HEADER(DEBRIS_UPDATE);
5192 ADD_DATA_U16(objp->net_signature);
5196 // add any extra relevant data
5198 case DEBRIS_UPDATE_UPDATE:
5199 // ADD_DATA(objp->pos); // add position
5200 add_vector_data(data, &packet_size, objp->pos);
5201 ADD_ORIENT(objp->orient); // add orientation
5202 // ADD_DATA(objp->phys_info.vel); // add velocity
5203 add_vector_data(data, &packet_size, objp->phys_info.vel);
5204 // ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5205 add_vector_data(data, &packet_size, objp->phys_info.rotvel);
5208 multi_io_send_to_all(data, packet_size);
5211 void process_debris_update_packet(ubyte *data, header *hinfo)
5215 object bogus_object;
5217 int offset = HEADER_LENGTH;
5219 GET_DATA_U16(net_sig);
5223 objp = multi_get_network_object(net_sig);
5225 objp = &bogus_object;
5229 // update the object
5230 case DEBRIS_UPDATE_UPDATE:
5231 //GET_DATA(objp->pos);
5232 get_vector_data( data, &offset, objp->pos );
5234 GET_ORIENT(objp->orient);
5235 GET_DATA(objp->phys_info.vel);
5236 GET_DATA(objp->phys_info.rotvel);
5238 // simply remove it (no explosion)
5239 case DEBRIS_UPDATE_REMOVE:
5240 if(objp != &bogus_object){
5241 Assert(objp->type == OBJ_DEBRIS);
5242 obj_delete(OBJ_INDEX(objp));
5246 case DEBRIS_UPDATE_NUKE:
5247 if(objp != &bogus_object)
5248 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5256 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)
5258 ubyte data[MAX_PACKET_SIZE];
5260 int packet_size = 0;
5262 BUILD_HEADER(WSS_REQUEST_PACKET);
5264 // add the request information
5265 ADD_DATA_S16(player_id);
5266 ADD_DATA_S32(from_slot);
5267 ADD_DATA_S32(from_index);
5268 ADD_DATA_S32(to_slot);
5269 ADD_DATA_S32(to_index);
5270 ADD_DATA_S32(wl_ship_slot); // only used in weapons loadout
5271 ADD_DATA_S32(ship_class);
5274 // a standard request
5276 multi_io_send_reliable(Net_player, data, packet_size);
5278 // being routed through the standalone to the host of the game
5280 Assert(Game_mode & GM_STANDALONE_SERVER);
5281 multi_io_send_reliable(p, data, packet_size);
5285 void process_wss_request_packet(ubyte *data, header *hinfo)
5287 int offset = HEADER_LENGTH;
5288 int from_slot,from_index;
5289 int to_slot,to_index;
5291 int wl_ship_slot,ship_class;
5295 // determine who this request is from
5296 GET_DATA_S16(player_id);
5297 player_num = find_player_id(player_id);
5299 // read in the request data
5300 GET_DATA_S32(from_slot);
5301 GET_DATA_S32(from_index);
5302 GET_DATA_S32(to_slot);
5303 GET_DATA_S32(to_index);
5304 GET_DATA_S32(wl_ship_slot); // only used in weapons loadout
5305 GET_DATA_S32(ship_class); // only used in multi team select
5309 Assert(player_num != -1);
5310 if(player_num == -1){
5314 // if we're the standalone, we have to route this packet to the host of the game
5315 if(Game_mode & GM_STANDALONE_SERVER){
5316 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5318 // otherwise we're the host and should process the request
5321 case WSS_WEAPON_SELECT :
5322 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5324 case WSS_SHIP_SELECT :
5325 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5333 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5335 ubyte data[MAX_PACKET_SIZE],team;
5336 int packet_size = 0;
5338 Assert(size <= (MAX_PACKET_SIZE - 10));
5340 BUILD_HEADER(WSS_UPDATE_PACKET);
5342 // add the team/pool # this is for
5343 team = (ubyte)team_num;
5346 // add the data block size
5349 // add the data itself
5350 memcpy(data + packet_size,wss_data,size);
5351 packet_size += size;
5353 // if we're also the master of the game (not on a standalone)
5354 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5355 multi_io_send_to_all_reliable(data, packet_size);
5357 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5359 multi_io_send_reliable(Net_player, data, packet_size);
5363 void process_wss_update_packet(ubyte *data, header *hinfo)
5366 int size,player_index,idx;
5367 int offset = HEADER_LENGTH;
5369 // get the team/pool #
5372 // get the data size
5375 // if we're the standalone, then we should be routing this data to all the other clients
5376 if(Game_mode & GM_STANDALONE_SERVER){
5381 // determine where this came from
5382 player_index = find_player_id(hinfo->id);
5383 Assert(player_index != -1);
5384 if(player_index < 0){
5388 // route the packet (don't resend it to the host)
5389 for(idx=0;idx<MAX_PLAYERS;idx++){
5390 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5391 multi_io_send_reliable(&Net_players[idx], data, offset);
5395 // set the proper pool pointers
5396 ss_set_team_pointers((int)team);
5398 // read in the block of data, and apply it to the weapons/ship pools
5399 offset += restore_wss_data(data + offset);
5402 // set the pool pointers back to my own team
5403 ss_set_team_pointers(Net_player->p_info.team);
5405 // sync the interface if this was for my own team
5406 if((int)team == Net_player->p_info.team){
5407 multi_ts_sync_interface();
5414 // function to send firing information from the client to the server once they reach
5415 // the final sync screen.
5416 void send_firing_info_packet()
5418 ubyte data[MAX_PACKET_SIZE];
5420 ubyte plinked, sdual;
5422 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5424 BUILD_HEADER(FIRING_INFO);
5425 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5426 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5427 ADD_DATA( plinked );
5430 multi_io_send_reliable(Net_player, data, packet_size);
5433 void process_firing_info_packet( ubyte *data, header *hinfo )
5435 int offset, player_num;
5436 ubyte plinked, sdual;
5439 // only the master of the game should be dealing with these packets
5440 Assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5442 offset = HEADER_LENGTH;
5443 GET_DATA( plinked );
5447 player_num = find_player_id(hinfo->id);
5449 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5453 // get the ship pointer for this player and set the flags accordingly.
5454 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5456 shipp->flags |= SF_PRIMARY_LINKED;
5458 shipp->flags &= ~SF_PRIMARY_LINKED;
5461 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5463 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5466 // packet to deal with changing status of mission goals. used to be sent every so often from server
5467 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5468 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5469 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5470 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5472 ubyte data[MAX_PACKET_SIZE];
5475 BUILD_HEADER(MISSION_GOAL_INFO);
5477 ADD_DATA_S32(goal_num);
5478 ADD_DATA_S32(new_status);
5479 ADD_DATA_S32(valid);
5481 multi_io_send_to_all_reliable(data, packet_size);
5484 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5486 int offset, goal_num, new_status, valid;
5488 offset = HEADER_LENGTH;
5489 GET_DATA_S32(goal_num);
5490 GET_DATA_S32(new_status);
5491 GET_DATA_S32(valid);
5494 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5495 if ( new_status != -1 ){
5496 mission_goal_status_change( goal_num, new_status );
5498 mission_goal_validation_change( goal_num, valid );
5502 void send_player_settings_packet(net_player *p)
5504 ubyte data[MAX_PACKET_SIZE];
5507 int packet_size = 0;
5510 BUILD_HEADER(PLAYER_SETTINGS);
5512 // add all the data for all the players
5514 for(idx=0;idx<MAX_PLAYERS;idx++){
5515 if(MULTI_CONNECTED(Net_players[idx])){
5517 ADD_DATA_S16(Net_players[idx].player_id);
5519 // break the p_info structure by member, so we don't overwrite any absolute pointers
5520 // ADD_DATA(Net_players[idx].p_info);
5521 ADD_DATA_S32(Net_players[idx].p_info.team);
5522 ADD_DATA_S32(Net_players[idx].p_info.ship_index);
5523 ADD_DATA_S32(Net_players[idx].p_info.ship_class);
5526 // add the stop byte
5530 // either broadcast the data or send to a specific player
5532 multi_io_send_to_all_reliable(data, packet_size);
5534 multi_io_send_reliable(p, data, packet_size);
5538 void process_player_settings_packet(ubyte *data, header *hinfo)
5540 int offset,player_num;
5541 net_player_info bogus,*ptr;
5545 offset = HEADER_LENGTH;
5547 // read in the data for all the players
5549 while(stop != 0xff){
5550 // lookup the player
5551 GET_DATA_S16(player_id);
5552 player_num = find_player_id(player_id);
5554 // make sure this is a valid player
5555 if(player_num == -1){
5558 ptr = &Net_players[player_num].p_info;
5561 GET_DATA_S32(ptr->team);
5562 GET_DATA_S32(ptr->ship_index);
5563 GET_DATA_S32(ptr->ship_class);
5570 // update the server with my new state
5571 // MWA -- 3/31/98 -- check for in mission instead of state.
5572 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5573 if( !(Game_mode & GM_IN_MISSION) ) {
5574 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5575 send_netplayer_update_packet();
5579 // display some cool text
5580 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5583 void send_deny_packet(net_addr *addr, int code)
5586 int packet_size = 0;
5588 // build the header and add the rejection code
5594 psnet_send(addr, data, packet_size);
5597 void process_deny_packet(ubyte *data, header *hinfo)
5601 // get the denial code
5602 offset = HEADER_LENGTH;
5606 // if there is already a dialog active, do nothing - who cares at this point.
5611 // display the appropriate dialog
5613 case JOIN_DENY_JR_STATE :
5614 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));
5616 case JOIN_DENY_JR_TRACKER_INVAL :
5617 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));
5619 case JOIN_DENY_JR_PASSWD :
5620 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5622 case JOIN_DENY_JR_CLOSED :
5623 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));
5625 case JOIN_DENY_JR_TEMP_CLOSED :
5626 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));
5628 case JOIN_DENY_JR_RANK_HIGH :
5629 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));
5631 case JOIN_DENY_JR_RANK_LOW :
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 low",728));
5634 case JOIN_DENY_JR_DUP :
5635 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5637 case JOIN_DENY_JR_FULL :
5638 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5640 case JOIN_DENY_JR_BANNED :
5641 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5643 case JOIN_DENY_JR_NOOBS :
5644 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5646 case JOIN_DENY_JR_INGAME_JOIN :
5647 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));
5649 case JOIN_DENY_JR_BAD_VERSION :
5650 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));
5652 case JOIN_DENY_JR_TYPE :
5653 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5657 // call this so that the join request timestamp automatically expires when we hear back from the server
5658 multi_join_reset_join_stamp();
5661 // this packet will consist of
5662 // 1.) netplayer ship classes (85 bytes max)
5663 // 2.) ship weapon state data (241 bytes max)
5664 // 3.) player settings et. al. (133 bytes max)
5665 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5666 void send_post_sync_data_packet(net_player *p, int std_request)
5668 ubyte data[MAX_PACKET_SIZE], val;
5673 ushort sval, ship_ets;
5674 int idx, player_index;
5675 int packet_size = 0;
5679 BUILD_HEADER(POST_SYNC_DATA);
5681 // some header information for standalone packet routing purposes
5682 val = (ubyte)std_request;
5685 // the standalone has two situations
5686 // 1.) sending a request to the host to distribute this block of data
5687 // 2.) having recevied this block of data from the host, it redistributes it
5688 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5689 // case 1, send the request
5690 multi_io_send_reliable(Netgame.host, data, packet_size);
5693 // case 2 for the standalone is below (as normal)
5695 // otherwise build the data now
5697 // add all deleted ships
5698 val = (ubyte)Multi_ts_num_deleted;
5700 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5701 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5707 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5708 shipp = &Ships[Objects[so->objnum].instance];
5710 // don't process non player wing ships
5711 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5717 // # of ships - used multiple times in the packet
5718 val = (ubyte)ship_count;
5721 // add ship class information (85 bytes max)
5722 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5723 shipp = &Ships[Objects[so->objnum].instance];
5725 // don't process non player wing ships
5726 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5729 // add the net signature of the object for look up
5730 ADD_DATA_U16( Objects[so->objnum].net_signature );
5732 // add the ship info index
5733 val = (ubyte)(shipp->ship_info_index);
5736 // add the ships's team select index
5737 val = (ubyte)shipp->ts_index;
5741 // add weapon state information for all starting ships (241 bytes max)
5742 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5743 shipp = &Ships[Objects[so->objnum].instance];
5745 // don't process non player wing ships
5746 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5749 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5751 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5752 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5753 if(player_index == -1){
5756 pl = &Net_players[player_index];
5760 // add the net signature and other weapon information
5761 ADD_DATA_U16( Objects[so->objnum].net_signature );
5763 // add number of primary and secondary banks
5764 bval = (char)(shipp->weapons.num_primary_banks);
5766 bval = (char)(shipp->weapons.num_secondary_banks);
5769 // add weapon bank status
5770 bval = (char)(shipp->weapons.current_primary_bank);
5772 pl->s_info.cur_primary_bank = bval;
5774 // Assert(bval != -1);
5777 bval = (char)(shipp->weapons.current_secondary_bank);
5779 pl->s_info.cur_secondary_bank = bval;
5781 // Assert(bval != -1);
5784 // primary weapon info
5785 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5787 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5790 // secondary weapon info
5791 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5793 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5794 ADD_DATA_S16(val_short);
5795 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5797 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5798 ADD_DATA_S16(val_short);
5799 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5801 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5802 ADD_DATA_S16(val_short);
5804 // send primary and secondary weapon link status
5806 if(shipp->flags & SF_PRIMARY_LINKED){
5808 pl->s_info.cur_link_status |= (1<<0);
5812 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5814 pl->s_info.cur_link_status |= (1<<1);
5818 // if this is a player ship add (1<<2)
5819 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5824 // add a ship ets value
5827 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5829 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5831 ship_ets |= ((ushort)shipp->engine_recharge_index);
5832 ADD_DATA_U16(ship_ets);
5836 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5837 // or if I'm the server as well as the host, I should be sending this to all players
5838 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5839 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5842 multi_io_send_to_all_reliable(data, packet_size);
5844 // send to a specific player
5846 multi_io_send_reliable(p, data, packet_size);
5849 multi_io_send_reliable(Net_player, data, packet_size);
5856 multi_io_send_to_all_reliable(data, packet_size);
5858 // send to a specific player
5860 multi_io_send_reliable(p, data, packet_size);
5865 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5867 ubyte val, sinfo_index, ts_index;
5869 ushort net_sig, ship_ets, sval;
5873 int offset = HEADER_LENGTH;
5877 // packet routing information
5880 // 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
5881 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5884 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5885 multi_ts_create_wings();
5887 // send to the standalone through my socket
5888 send_post_sync_data_packet(Net_player);
5894 // add all deleted ships
5896 Multi_ts_num_deleted = (int)val;
5897 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5898 // get the ship's objnum
5901 objp = multi_get_network_object(sval);
5903 // delete the ship appropriately
5904 // mark the object as having been deleted
5905 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5908 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5909 obj_delete(Multi_ts_deleted_objnums[idx]);
5910 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5912 Multi_ts_num_deleted--;
5913 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5921 // process ship class information
5922 for(idx=0; idx<ship_count; idx++){
5923 // get the object's net signature
5924 GET_DATA_U16(net_sig);
5925 GET_DATA(sinfo_index);
5928 // attempt to get the object
5929 objp = multi_get_network_object(net_sig);
5931 // make sure we found a ship
5932 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5934 // set the ship to be the right class
5935 change_ship_type(objp->instance,(int)sinfo_index);
5937 // set the ship's team select index
5938 Ships[objp->instance].ts_index = (int)ts_index;
5941 // process ship weapon state info
5942 for(idx=0; idx<ship_count; idx++){
5943 // get the object's net signature
5944 GET_DATA_U16(net_sig);
5946 // attempt to get the object
5947 objp = multi_get_network_object(net_sig);
5949 // make sure we found a ship
5950 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5952 // get a pointer to the ship
5953 shipp = &Ships[objp->instance];
5955 // get number of primary and secondary banks;
5958 shipp->weapons.num_primary_banks = (int)b;
5962 shipp->weapons.num_secondary_banks = (int)b;
5964 // get bank selection info
5969 shipp->weapons.current_primary_bank = (int)b;
5975 shipp->weapons.current_secondary_bank = (int)b;
5977 // primary weapon info
5979 shipp->weapons.primary_bank_weapons[0] = (int)b;
5982 shipp->weapons.primary_bank_weapons[1] = (int)b;
5984 // secondary weapon info
5986 shipp->weapons.secondary_bank_weapons[0] = (int)b;
5987 GET_DATA_S16(val_short);
5988 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
5991 shipp->weapons.secondary_bank_weapons[1] = (int)b;
5992 GET_DATA_S16(val_short);
5993 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
5996 shipp->weapons.secondary_bank_weapons[2] = (int)b;
5997 GET_DATA_S16(val_short);
5998 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
6005 shipp->flags |= SF_PRIMARY_LINKED;
6008 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
6010 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
6011 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
6013 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
6015 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
6019 GET_DATA_U16(ship_ets);
6021 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
6023 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
6025 shipp->engine_recharge_index = (ship_ets & 0x000f);
6030 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
6031 send_netplayer_update_packet();
6033 // the standalone server will receive this packet from the host of the game, to be applied locally and
6034 // also to be rebroadcast.
6035 if(Game_mode & GM_STANDALONE_SERVER){
6036 // update player ets settings
6037 for(idx=0;idx<MAX_PLAYERS;idx++){
6038 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
6039 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
6043 send_post_sync_data_packet(NULL,0);
6047 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
6049 ubyte data[MAX_PACKET_SIZE],val;
6052 int packet_size = 0;
6055 BUILD_HEADER(WSS_SLOTS_DATA);
6057 // some header information for standalone packet routing purposes
6058 val = (ubyte)std_request;
6062 val = (ubyte)team_num;
6065 // add whether this is the final packet or not
6069 // the standalone has two situations
6070 // 1.) sending a request to the host to distribute this block of data
6071 // 2.) having recevied this block of data from the host, it redistributes it
6072 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
6073 // case 1, send the request
6074 multi_io_send_reliable(Netgame.host, data, packet_size);
6077 // case 2 for the standalone is below (as normal)
6079 // add all the slots
6080 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6081 // add the ship class
6082 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
6086 for(i = 0;i<MAX_WL_WEAPONS;i++){
6087 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
6091 // add the weapon counts
6092 for(i = 0;i<MAX_WL_WEAPONS;i++){
6093 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
6094 ADD_DATA_S16(val_short);
6098 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
6099 // or if I'm the server as well as the host, I should be sending this to all players
6100 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
6101 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6104 multi_io_send_to_all_reliable(data, packet_size);
6106 // send to a specific player
6108 multi_io_send_reliable(p, data, packet_size);
6111 multi_io_send_reliable(Net_player, data, packet_size);
6118 multi_io_send_to_all_reliable(data, packet_size);
6120 // send to a specific player
6122 multi_io_send_reliable(p, data, packet_size);
6127 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6129 ubyte val,team_num,final;
6131 int offset = HEADER_LENGTH;
6134 // packet routing information
6140 // get whether this is the final packet or not
6143 // 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
6144 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6147 // send to the standalone through my socket
6148 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6152 // read in all the slot data
6153 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6154 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6156 // get the ship class
6158 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6161 for(i = 0;i<MAX_WL_WEAPONS;i++){
6163 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6165 // check for signed/unsigned problems
6166 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6167 Wss_slots_teams[team_num][idx].wep[i] = -1;
6171 // get the weapon counts
6172 for(i = 0;i<MAX_WL_WEAPONS;i++){
6173 GET_DATA_S16(val_short);
6174 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6179 // update my netplayer state if this is the final packet
6181 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6182 send_netplayer_update_packet();
6185 // the standalone server will receive this packet from the host of the game, to be applied locally and
6186 // also to be rebroadcast.
6187 if(Game_mode & GM_STANDALONE_SERVER){
6188 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6190 // add some mission sync text
6191 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6195 #define OBJ_VISIBILITY_DOT 0.6f
6197 // send and receive packets for shield explosion information
6198 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6201 ubyte data[MAX_PACKET_SIZE], utri_num;
6204 // Assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6206 Assert( tri_num < UCHAR_MAX );
6207 utri_num = (ubyte)tri_num;
6209 // for each player, determine if this object is behind the player -- if so, don't
6211 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6212 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6214 vector eye_to_obj_vec, diff, eye_pos;
6217 eye_pos = Net_players[i].s_info.eye_pos;
6218 eye_orient = Net_players[i].s_info.eye_orient;
6220 // check for same vectors
6221 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6222 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6226 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6227 dot = vm_vec_dot(&eye_orient.v.fvec, &eye_to_obj_vec);
6229 if ( dot < OBJ_VISIBILITY_DOT ){
6233 BUILD_HEADER(SHIELD_EXPLOSION);
6235 ADD_DATA_U16( Objects[objnum].net_signature );
6238 multi_io_send(&Net_players[i], data, packet_size);
6243 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6245 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6252 // get the shield hit data
6253 offset = HEADER_LENGTH;
6254 GET_DATA_U16(signature);
6256 //GET_DATA(hit_pos);
6259 // find the object with this signature. If found, then do a shield explosion
6260 objp = multi_get_network_object( signature );
6263 shield_info *shieldp;
6268 // given the tri num, find the local position which is the average of the
6269 // three vertices of the triangle affected. Use this average point as the hit
6271 // Assert( objp->type == OBJ_SHIP );
6272 if(objp->type != OBJ_SHIP){
6276 pm = model_get(Ships[objp->instance].modelnum);
6277 shieldp = &pm->shield;
6278 Assert( utri_num < shieldp->ntris );
6279 stri = shieldp->tris[utri_num];
6280 vm_vec_zero(&hit_pos);
6281 for ( i = 0; i < 3; i++ ) {
6282 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6284 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6285 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6289 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6292 ubyte data[MAX_PACKET_SIZE], val;
6294 int packet_size = 0;
6299 sc = &pl->player->stats;
6302 BUILD_HEADER(PLAYER_STATS);
6304 // add the player id
6305 ADD_DATA_S16(pl->player_id);
6307 // add the byte indicating whether these stats are all-time or not
6308 val = (ubyte)stats_code;
6311 // kill information - alltime
6315 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6316 u_tmp = sc->kills[idx];
6317 ADD_DATA_U16(u_tmp);
6319 // medal information
6320 for(idx=0;idx<NUM_MEDALS;idx++){
6321 i_tmp = sc->medals[idx];
6322 ADD_DATA_S32(i_tmp);
6325 ADD_DATA_S32(sc->score);
6326 ADD_DATA_S32(sc->rank);
6327 ADD_DATA_S32(sc->assists);
6328 ADD_DATA_S32(sc->kill_count);
6329 ADD_DATA_S32(sc->kill_count_ok);
6330 ADD_DATA_U32(sc->p_shots_fired);
6331 ADD_DATA_U32(sc->s_shots_fired);
6332 ADD_DATA_U32(sc->p_shots_hit);
6333 ADD_DATA_U32(sc->s_shots_hit);
6334 ADD_DATA_U32(sc->p_bonehead_hits);
6335 ADD_DATA_U32(sc->s_bonehead_hits);
6336 ADD_DATA_S32(sc->bonehead_kills);
6338 ADD_DATA_U32(sc->missions_flown);
6339 ADD_DATA_U32(sc->flight_time);
6340 ADD_DATA_S32(sc->last_flown);
6341 ADD_DATA_S32(sc->last_backup);
6346 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6347 u_tmp = sc->m_okKills[idx];
6348 ADD_DATA_U16(u_tmp);
6351 ADD_DATA_S32(sc->m_score);
6352 ADD_DATA_S32(sc->m_assists);
6353 ADD_DATA_S32(sc->m_kill_count);
6354 ADD_DATA_S32(sc->m_kill_count_ok);
6355 ADD_DATA_U32(sc->mp_shots_fired);
6356 ADD_DATA_U32(sc->ms_shots_fired);
6357 ADD_DATA_U32(sc->mp_shots_hit);
6358 ADD_DATA_U32(sc->ms_shots_hit);
6359 ADD_DATA_U32(sc->mp_bonehead_hits);
6360 ADD_DATA_U32(sc->ms_bonehead_hits);
6361 ADD_DATA_S32(sc->m_bonehead_kills);
6362 ADD_DATA_S32(sc->m_player_deaths);
6363 ADD_DATA_S32(sc->m_medal_earned);
6366 case STATS_MISSION_KILLS:
6367 ADD_DATA_S32(sc->m_kill_count);
6368 ADD_DATA_S32(sc->m_kill_count_ok);
6369 ADD_DATA_S32(sc->m_assists);
6372 case STATS_DOGFIGHT_KILLS:
6373 for(idx=0; idx<MAX_PLAYERS; idx++){
6374 u_tmp = sc->m_dogfight_kills[idx];
6375 ADD_DATA_U16(u_tmp);
6377 ADD_DATA_S32(sc->m_kill_count);
6378 ADD_DATA_S32(sc->m_kill_count_ok);
6379 ADD_DATA_S32(sc->m_assists);
6383 Assert(packet_size < MAX_PACKET_SIZE);
6385 // if we're a client, always send the data to the server
6386 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6387 multi_io_send_reliable(Net_player, data, packet_size);
6389 // otherwise do server specific stuff
6391 // send to a specific target
6393 multi_io_send_reliable(target, data, packet_size);
6395 // otherwise, send to everyone
6397 multi_io_send_to_all_reliable(data, packet_size);
6402 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6406 scoring_struct *sc,bogus;
6408 int offset = HEADER_LENGTH;
6412 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6414 // get the player who these stats are for
6415 GET_DATA_S16(player_id);
6416 player_num = find_player_id(player_id);
6417 if (player_num == -1) {
6418 nprintf(("Network", "Couldn't find player for stats update!\n"));
6419 ml_string("Couldn't find player for stats update!\n");
6424 sc = &Net_players[player_num].player->stats;
6427 // get the stats code
6431 ml_string("Received STATS_ALLTIME\n");
6434 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6435 GET_DATA_U16(u_tmp);
6436 sc->kills[idx] = u_tmp;
6439 // read in the stats
6440 for (idx=0; idx<NUM_MEDALS; idx++) {
6441 GET_DATA_S32(i_tmp);
6442 sc->medals[idx] = i_tmp;
6445 GET_DATA_S32(sc->score);
6446 GET_DATA_S32(sc->rank);
6447 GET_DATA_S32(sc->assists);
6448 GET_DATA_S32(sc->kill_count);
6449 GET_DATA_S32(sc->kill_count_ok);
6450 GET_DATA_U32(sc->p_shots_fired);
6451 GET_DATA_U32(sc->s_shots_fired);
6452 GET_DATA_U32(sc->p_shots_hit);
6453 GET_DATA_U32(sc->s_shots_hit);
6454 GET_DATA_U32(sc->p_bonehead_hits);
6455 GET_DATA_U32(sc->s_bonehead_hits);
6456 GET_DATA_S32(sc->bonehead_kills);
6458 GET_DATA_U32(sc->missions_flown);
6459 GET_DATA_U32(sc->flight_time);
6460 GET_DATA_S32(sc->last_flown);
6461 GET_DATA_S32(sc->last_backup);
6465 ml_string("Received STATS_MISSION\n");
6467 // kills - mission OK
6468 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6469 GET_DATA_U16(u_tmp);
6470 sc->m_okKills[idx] = u_tmp;
6473 GET_DATA_S32(sc->m_score);
6474 GET_DATA_S32(sc->m_assists);
6475 GET_DATA_S32(sc->m_kill_count);
6476 GET_DATA_S32(sc->m_kill_count_ok);
6477 GET_DATA_U32(sc->mp_shots_fired);
6478 GET_DATA_U32(sc->ms_shots_fired);
6479 GET_DATA_U32(sc->mp_shots_hit);
6480 GET_DATA_U32(sc->ms_shots_hit);
6481 GET_DATA_U32(sc->mp_bonehead_hits);
6482 GET_DATA_U32(sc->ms_bonehead_hits);
6483 GET_DATA_S32(sc->m_bonehead_kills);
6484 GET_DATA_S32(sc->m_player_deaths);
6485 GET_DATA_S32(sc->m_medal_earned);
6488 case STATS_MISSION_KILLS:
6489 ml_string("Received STATS_MISSION_KILLS\n");
6491 GET_DATA_S32(sc->m_kill_count);
6492 GET_DATA_S32(sc->m_kill_count_ok);
6493 GET_DATA_S32(sc->m_assists);
6496 case STATS_DOGFIGHT_KILLS:
6497 ml_string("Received STATS_DOGFIGHT_KILLS\n");
6498 if(player_num >= 0){
6499 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6501 for(idx=0; idx<MAX_PLAYERS; idx++){
6502 GET_DATA_U16(u_tmp);
6503 sc->m_dogfight_kills[idx] = u_tmp;
6504 if(player_num >= 0){
6505 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6508 GET_DATA_S32(sc->m_kill_count);
6509 GET_DATA_S32(sc->m_kill_count_ok);
6510 GET_DATA_S32(sc->m_assists);
6515 // if I'm the server of the game, I should always rebroadcast these stats
6516 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6517 // make sure these are alltime stats
6518 Assert(val == STATS_ALLTIME);
6520 multi_broadcast_stats(STATS_ALLTIME);
6524 // called to create asteroid stuff
6525 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6528 ubyte data[MAX_PACKET_SIZE];
6529 ubyte packet_type, atype;
6533 if (relvec != NULL )
6536 BUILD_HEADER( ASTEROID_INFO );
6537 packet_type = ASTEROID_CREATE;
6539 Assert( asteroid_type < UCHAR_MAX );
6540 atype = (ubyte)asteroid_type;
6542 ADD_DATA( packet_type );
6543 ADD_DATA_U16( parent_objp->net_signature );
6544 ADD_DATA_U16( new_objp->net_signature );
6547 add_vector_data( data, &packet_size, vec );
6549 multi_io_send_to_all(data, packet_size);
6552 void send_asteroid_throw( object *objp )
6555 ubyte data[MAX_PACKET_SIZE], packet_type;
6557 BUILD_HEADER( ASTEROID_INFO );
6559 // this packet type is an asteroid throw
6560 packet_type = ASTEROID_THROW;
6561 ADD_DATA( packet_type );
6562 ADD_DATA_U16( objp->net_signature );
6563 //ADD_DATA( objp->pos );
6564 add_vector_data( data, &packet_size, objp->pos );
6565 //ADD_DATA( objp->phys_info.vel );
6566 add_vector_data( data, &packet_size, objp->phys_info.vel );
6568 multi_io_send_to_all(data, packet_size);
6571 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6574 ubyte data[MAX_PACKET_SIZE], packet_type;
6578 if ( hitpos != NULL )
6581 // build up an asteroid hit packet
6582 BUILD_HEADER( ASTEROID_INFO );
6583 packet_type = ASTEROID_HIT;
6584 ADD_DATA( packet_type );
6585 ADD_DATA_U16( objp->net_signature );
6587 if(other_objp == NULL){
6588 ushort invalid_sig = 0xffff;
6589 ADD_DATA_U16(invalid_sig);
6591 ADD_DATA_U16( other_objp->net_signature );
6594 add_vector_data( data, &packet_size, vec );
6595 ADD_DATA_FL( damage );
6597 multi_io_send_to_all(data, packet_size);
6600 void process_asteroid_info( ubyte *data, header *hinfo )
6605 offset = HEADER_LENGTH;
6606 GET_DATA( packet_type );
6608 // based on the packet type, do something interesting with an asteroid!
6609 switch( packet_type ) {
6611 case ASTEROID_CREATE: {
6612 ushort psignature, signature;
6615 object *parent_objp;
6617 GET_DATA_U16( psignature );
6618 GET_DATA_U16( signature );
6620 //GET_DATA( relvec );
6621 get_vector_data( data, &offset, relvec );
6623 // after getting the values, set the next network signature, and call the create sub function
6624 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6625 parent_objp = multi_get_network_object( psignature );
6626 if ( parent_objp ) {
6627 asteroid_sub_create( parent_objp, atype, &relvec );
6629 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6636 // asteroid throw packet -- asteroid has wrapped bounds
6637 case ASTEROID_THROW: {
6642 GET_DATA_U16( signature );
6644 get_vector_data( data, &offset, pos );
6646 get_vector_data( data, &offset, vel );
6647 objp = multi_get_network_object( signature );
6649 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6653 objp->phys_info.vel = vel;
6654 objp->phys_info.desired_vel = vel;
6658 case ASTEROID_HIT: {
6659 ushort signature, osignature;
6660 object *objp, *other_objp;
6664 GET_DATA_U16( signature );
6665 GET_DATA_U16( osignature );
6666 //GET_DATA( hitpos );
6667 get_vector_data( data, &offset, hitpos );
6668 GET_DATA_FL( damage );
6670 objp = multi_get_network_object( signature );
6671 if(osignature == 0xffff){
6674 other_objp = multi_get_network_object( osignature );
6677 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6681 if ( IS_VEC_NULL(&hitpos) ){
6682 asteroid_hit( objp, other_objp, NULL, damage );
6684 asteroid_hit( objp, other_objp, &hitpos, damage );
6687 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6688 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6689 weapon_hit( other_objp, objp, &hitpos );
6702 void send_host_restr_packet(char *callsign,int code,int mode)
6704 ubyte data[MAX_PACKET_SIZE],val;
6705 int packet_size = 0;
6707 // build the header and add the opcode
6708 BUILD_HEADER(HOST_RESTR_QUERY);
6715 ADD_STRING(callsign);
6717 // if I'm the standalone server, I should be sending this to the game host
6718 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6719 multi_io_send_reliable(Netgame.host, data, packet_size);
6721 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6723 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6724 multi_io_send_reliable(Net_player, data, packet_size);
6728 void process_host_restr_packet(ubyte *data, header *hinfo)
6732 int offset = HEADER_LENGTH;
6734 // get the opcode and the callsign
6737 GET_STRING(callsign);
6740 // do code specific operations
6742 // query to the host from standalone
6744 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6746 // set the join mode
6747 Multi_join_restr_mode = mode;
6749 // set the timestamp
6750 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6752 // notify the host of the event
6753 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6754 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6757 // affirmative reply from the host to the standalone
6759 Assert(Game_mode & GM_STANDALONE_SERVER);
6761 // let the player join if the timestamp has not already elapsed on the server
6762 if(Multi_restr_query_timestamp != -1){
6763 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6769 Assert(Game_mode & GM_STANDALONE_SERVER);
6770 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6771 Multi_restr_query_timestamp = -1;
6776 void send_netgame_end_error_packet(int notify_code,int err_code)
6780 int packet_size = 0;
6782 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6783 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6785 // build the header and add the notification and error codes
6786 BUILD_HEADER(NETGAME_END_ERROR);
6787 code = (char)notify_code;
6789 code = (char)err_code;
6793 multi_io_send_to_all_reliable(data, packet_size);
6796 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6798 int offset = HEADER_LENGTH;
6799 char notify_code,error_code;
6801 // get the error and notification codes
6802 GET_DATA(notify_code);
6803 GET_DATA(error_code);
6807 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6810 // sends info that a countermeasure succeeded.
6811 void send_countermeasure_success_packet( int objnum )
6813 int pnum, packet_size;
6814 ubyte data[MAX_PACKET_SIZE];
6816 pnum = multi_find_player_by_object( &Objects[objnum] );
6818 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6822 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6823 multi_io_send(&Net_players[pnum], data, packet_size);
6826 // start the flashing of my hud gauge
6827 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6831 offset = HEADER_LENGTH;
6834 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6835 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6838 #define UPDATE_IS_PAUSED (1<<0)
6839 #define UPDATE_HULL_INFO (1<<1)
6841 void send_client_update_packet(net_player *pl)
6843 ubyte data[MAX_PACKET_SIZE],val;
6844 int packet_size = 0;
6847 BUILD_HEADER(CLIENT_UPDATE);
6851 // add the pause status
6852 if ( Multi_pause_status ) {
6853 val |= UPDATE_IS_PAUSED;
6854 } 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) ) {
6855 val |= UPDATE_HULL_INFO;
6856 Assert( Player_ship ); // I"d better have one of these!!!!
6861 // if paused, add the net address of the guy who paused
6862 if(val & UPDATE_IS_PAUSED){
6863 Assert(Multi_pause_pauser != NULL);
6864 ADD_DATA_S16(Multi_pause_pauser->player_id);
6867 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6868 if ( val & UPDATE_HULL_INFO ) {
6870 ubyte percent, ns, threats;
6873 ship_subsys *subsysp;
6876 // get the object for the player
6877 Assert( pl->player->objnum != -1 );
6879 objp = &Objects[pl->player->objnum];
6881 Assert ( objp->type == OBJ_SHIP );
6882 shipp = &Ships[objp->instance];
6883 sip = &Ship_info[shipp->ship_info_index];
6885 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6886 // percentage value since that should be close enough
6887 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6891 percent = (ubyte)temp;
6893 ADD_DATA( percent );
6895 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6896 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6897 ADD_DATA( percent );
6900 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6901 // both machines. Added as percent since that number <= 100
6903 // also write out the number of subsystems. We do this because the client might not know
6904 // about the object he is getting data for. (i.e. he killed the object already).
6905 ns = (ubyte)sip->n_subsystems;
6908 // now the subsystems.
6909 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6910 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6911 ADD_DATA( percent );
6914 // compute the threats for this player. Only compute the threats if this player is actually
6915 // playing (i.e. he has a ship)
6916 hud_update_reticle( pl->player );
6917 threats = (ubyte)pl->player->threat_flags;
6918 ADD_DATA( threats );
6920 // add his energy level for guns
6921 ADD_DATA_FL(shipp->weapon_energy);
6923 // add his secondary bank ammo
6924 ADD_DATA_S32(shipp->weapons.num_secondary_banks);
6925 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6926 ADD_DATA_S32(shipp->weapons.secondary_bank_ammo[i]);
6931 ADD_DATA_S32(pl->sv_last_pl);
6933 // send the packet reliably to the player
6934 multi_io_send(pl, data, packet_size);
6937 void process_client_update_packet(ubyte *data, header *hinfo)
6942 int is_paused, have_hull_info;
6945 float weapon_energy;
6946 int offset = HEADER_LENGTH;
6948 // get the header byte containing useful information
6951 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6952 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6954 // if we are paused, get who paused
6956 GET_DATA_S16(pauser);
6957 player_index = find_player_id(pauser);
6958 if(player_index != -1){
6959 Multi_pause_pauser = &Net_players[player_index];
6961 Multi_pause_pauser = NULL;
6965 // if we have hull information, then read it in.
6966 if ( have_hull_info ) {
6970 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
6972 ship_subsys *subsysp;
6976 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6977 // percentage value since that should be close enough
6978 GET_DATA( hull_percent );
6980 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
6982 shield_percent[i] = ub_tmp;
6985 // get the data for the subsystems
6986 GET_DATA( n_subsystems );
6987 for ( i = 0; i < n_subsystems; i++ ){
6989 subsystem_percent[i] = ub_tmp;
6992 GET_DATA( threats );
6994 // add his energy level for guns
6995 GET_DATA_FL(weapon_energy);
6997 // add his secondary bank ammo
6998 GET_DATA_S32(ammo_count);
6999 for(i=0; i<ammo_count; i++){
7000 GET_DATA_S32(ammo[i]);
7003 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
7004 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
7005 // UDP client update packets. Only read this info if I have a ship.
7006 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
7007 shipp = Player_ship;
7009 sip = &Ship_info[shipp->ship_info_index];
7011 val = hull_percent * sip->initial_hull_strength / 100.0f;
7012 objp->hull_strength = val;
7014 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
7015 val = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
7016 objp->shields[i] = val;
7019 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
7020 // then don't read these in.
7021 if ( n_subsystems == sip->n_subsystems ) {
7023 n_subsystems = 0; // reuse this variable
7024 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
7027 val = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
7028 subsysp->current_hits = val;
7030 // add the value just generated (it was zero'ed above) into the array of generic system types
7031 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
7032 Assert ( subsys_type < SUBSYSTEM_MAX );
7033 shipp->subsys_info[subsys_type].current_hits += val;
7037 ship_recalc_subsys_strength( shipp );
7039 shipp->weapon_energy = weapon_energy;
7040 for(i=0; i<ammo_count; i++){
7041 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
7044 // update my threat flags.
7045 // temporarily commented out until tested.
7046 Net_player->player->threat_flags = threats;
7053 if(Net_player != NULL){
7054 Net_player->cl_last_pl = pl;
7058 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
7059 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
7061 multi_pause_pause();
7063 multi_pause_unpause();
7068 void send_countdown_packet(int time)
7072 int packet_size = 0;
7074 // build the header and add the time
7075 BUILD_HEADER(COUNTDOWN);
7079 // if we're the server, we should broadcast to everyone
7080 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7081 multi_io_send_to_all_reliable(data, packet_size);
7083 // otherwise we'de better be a host sending to the standalone
7085 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
7086 multi_io_send_reliable(Net_player, data, packet_size);
7090 void process_countdown_packet(ubyte *data, header *hinfo)
7092 int offset = HEADER_LENGTH;
7099 // if we're not in the post sync data screen, ignore it
7100 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
7104 // if we're the standalone, this should be a -1 telling us to start the countdown
7105 if(Game_mode & GM_STANDALONE_SERVER){
7106 Assert((int)time == -1);
7108 // start the countdown
7109 multi_sync_start_countdown();
7111 // otherwise if we're clients, just bash the countdown
7113 Multi_sync_countdown = (int)time;
7117 // packets for debriefing information
7118 void send_debrief_info( int stage_count[], int *stages[] )
7120 ubyte data[MAX_PACKET_SIZE];
7121 int packet_size, i, j;
7124 BUILD_HEADER(DEBRIEF_INFO);
7126 // add the data for the teams
7127 for ( i = 0; i < Num_teams; i++ ) {
7130 count = stage_count[i];
7131 ADD_DATA_S32( count );
7132 for ( j = 0; j < count; j++ ) {
7133 i_tmp = stages[i][j];
7134 ADD_DATA_S32( i_tmp );
7138 multi_io_send_to_all_reliable(data, packet_size);
7141 // process a debrief info packet from the server
7142 void process_debrief_info( ubyte *data, header *hinfo )
7145 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7148 offset = HEADER_LENGTH;
7149 for ( i = 0; i < Num_teams; i++ ) {
7152 GET_DATA_S32( count );
7153 stage_counts[i] = count;
7154 stages[i] = active_stages[i];
7155 for ( j = 0; j < count; j++ ) {
7156 GET_DATA_S32(i_tmp);
7157 active_stages[i][j] = i_tmp;
7162 // now that we have the stage data for the debriefing stages, call debrief function with the
7163 // data so that clients can now see the debriefing stuff. Do it only for my team.
7164 Assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7165 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7168 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7169 // sends homing_object and homing_subsystem to all clients.
7170 void send_homing_weapon_info( int weapon_num )
7172 ubyte data[MAX_PACKET_SIZE];
7175 object *homing_object;
7176 ushort homing_signature;
7179 wp = &Weapons[weapon_num];
7181 // be sure that this weapon object is a homing object.
7182 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7185 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7186 // homing signature.
7187 homing_signature = 0;
7188 homing_object = wp->homing_object;
7189 if ( homing_object != NULL ) {
7190 homing_signature = homing_object->net_signature;
7192 // get the subsystem index.
7194 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7197 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7198 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
7199 t_subsys = (char)s_index;
7203 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7204 ADD_DATA_U16( Objects[wp->objnum].net_signature );
7205 ADD_DATA_U16( homing_signature );
7206 ADD_DATA( t_subsys );
7208 multi_io_send_to_all(data, packet_size);
7211 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7212 // packet contains information for multiple weapons (like hornets).
7213 void process_homing_weapon_info( ubyte *data, header *hinfo )
7216 ushort weapon_signature, homing_signature;
7218 object *homing_object, *weapon_objp;
7221 offset = HEADER_LENGTH;
7223 // get the data for the packet
7224 GET_DATA_U16( weapon_signature );
7225 GET_DATA_U16( homing_signature );
7226 GET_DATA( h_subsys );
7229 // deal with changing this weapons homing information
7230 weapon_objp = multi_get_network_object( weapon_signature );
7231 if ( weapon_objp == NULL ) {
7232 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7235 Assert( weapon_objp->type == OBJ_WEAPON );
7236 wp = &Weapons[weapon_objp->instance];
7238 // be sure that we can find these weapons and
7239 homing_object = multi_get_network_object( homing_signature );
7240 if ( homing_object == NULL ) {
7241 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7245 if ( homing_object->type == OBJ_WEAPON ) {
7246 Assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7249 wp->homing_object = homing_object;
7250 wp->homing_subsys = NULL;
7251 wp->target_num = OBJ_INDEX(homing_object);
7252 wp->target_sig = homing_object->signature;
7253 if ( h_subsys != -1 ) {
7254 Assert( homing_object->type == OBJ_SHIP );
7255 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7258 if ( homing_object->type == OBJ_SHIP ) {
7259 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7263 void send_emp_effect(ushort net_sig, float intensity, float time)
7268 Assert(MULTIPLAYER_MASTER);
7270 // build the packet and add the opcode
7271 BUILD_HEADER(EMP_EFFECT);
7272 ADD_DATA_U16(net_sig);
7273 ADD_DATA_FL(intensity);
7276 // send it to the player
7277 multi_io_send_to_all(data, packet_size);
7280 void process_emp_effect(ubyte *data, header *hinfo)
7282 float intensity, time;
7285 int offset = HEADER_LENGTH;
7287 // read in the EMP effect data
7288 GET_DATA_U16(net_sig);
7289 GET_DATA_FL(intensity);
7293 // try and find the object
7294 objp = multi_get_network_object(net_sig);
7295 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7296 // if i'm not an observer and I have a valid ship, play the EMP effect
7297 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7298 emp_start_local(intensity, time);
7301 // start the effect for the ship itself
7302 emp_start_ship(objp, intensity, time);
7306 // tells whether or not reinforcements are available
7307 void send_reinforcement_avail( int rnum )
7312 BUILD_HEADER(REINFORCEMENT_AVAIL);
7313 ADD_DATA_S32( rnum );
7314 multi_io_send_to_all_reliable(data, packet_size);
7317 void process_reinforcement_avail( ubyte *data, header *hinfo )
7322 offset = HEADER_LENGTH;
7323 GET_DATA_S32( rnum );
7326 // sanity check for a valid reinforcement number
7327 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7328 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7332 void send_change_iff_packet(ushort net_signature, int new_team)
7334 ubyte data[MAX_PACKET_SIZE];
7335 int packet_size = 0;
7337 if(Net_player == NULL){
7340 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7344 // build the packet and add the data
7345 BUILD_HEADER(CHANGE_IFF);
7346 ADD_DATA_U16(net_signature);
7347 ADD_DATA_S32(new_team);
7349 // send to all players
7350 multi_io_send_to_all_reliable(data, packet_size);
7353 void process_change_iff_packet( ubyte *data, header *hinfo)
7355 int offset = HEADER_LENGTH;
7356 ushort net_signature;
7361 GET_DATA_U16(net_signature);
7362 GET_DATA_S32(new_team);
7365 // lookup the object
7366 objp = multi_get_network_object(net_signature);
7367 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7368 Ships[objp->instance].team = new_team;
7372 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7374 int packet_size, objnum;
7375 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7378 net_player *ignore = NULL;
7380 // sanity checking for now
7381 Assert ( banks_fired <= 3 );
7383 // get an object pointer for this ship.
7384 objnum = shipp->objnum;
7385 objp = &Objects[objnum];
7387 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7388 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7392 // just in case nothing got fired
7393 if(banks_fired <= 0){
7397 // ubanks_fired = (ubyte)banks_fired;
7398 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7399 // Assert( current_bank <= 3 );
7401 // insert the current primary bank into this byte
7402 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7404 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7405 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7406 // ubanks_fired |= (1<<7);
7409 // determine if its a player ship and don't send to him if we're in "client firing" mode
7410 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7411 if(MULTIPLAYER_MASTER){
7412 np_index = multi_find_player_by_net_signature(objp->net_signature);
7413 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7414 ignore = &Net_players[np_index];
7418 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7419 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7420 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7421 // and server in sync w.r.t. weapon energy for player ship
7422 BUILD_HEADER( PRIMARY_FIRED_NEW );
7423 ADD_DATA_U16(objp->net_signature);
7424 // ADD_DATA(ubanks_fired);
7426 // if I'm a server, broadcast to all players
7427 if(MULTIPLAYER_MASTER){
7428 multi_io_send_to_all(data, packet_size, ignore);
7431 multi_rate_add(1, "wfi", packet_size);
7433 // otherwise just send to the server
7435 multi_io_send(Net_player, data, packet_size);
7439 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7441 int offset; // linked;
7442 // ubyte banks_fired, current_bank;
7447 // read all packet info
7448 offset = HEADER_LENGTH;
7449 GET_DATA_U16(shooter_sig);
7450 // GET_DATA(banks_fired);
7453 // find the object this fired packet is operating on
7454 objp = multi_get_network_object( shooter_sig );
7455 if ( objp == NULL ) {
7456 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7459 // if this object is not actually a valid ship, don't do anything
7460 if(objp->type != OBJ_SHIP){
7463 if(objp->instance < 0){
7466 shipp = &Ships[objp->instance];
7468 // get the link status of the primary banks
7470 // if ( banks_fired & (1<<7) ) {
7472 // banks_fired ^= (1<<7);
7475 // get the current primary bank
7476 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7477 // current_bank &= 0x3;
7478 // Assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7479 // shipp->weapons.current_primary_bank = current_bank;
7481 // strip off all remaining bits and just keep which banks were actually fired.
7482 // banks_fired &= 0x3;
7484 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7487 // shipp->flags &= ~SF_PRIMARY_LINKED;
7489 // shipp->flags |= SF_PRIMARY_LINKED;
7492 // if we're in client firing mode, ignore ones for myself
7493 if((Player_obj != NULL) && (Player_obj == objp)){
7497 ship_fire_primary( objp, 0, 1 );
7500 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7502 ubyte data[MAX_PACKET_SIZE];
7505 net_player *ignore = NULL;
7507 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7508 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7512 Assert ( cmeasure_count < UCHAR_MAX );
7513 BUILD_HEADER(COUNTERMEASURE_NEW);
7514 ADD_DATA_U16( objp->net_signature );
7515 ADD_DATA_S32( rand_val );
7517 nprintf(("Network","Sending NEW countermeasure packet!\n"));
7519 // determine if its a player ship and don't send to him if we're in "client firing" mode
7520 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7521 if(MULTIPLAYER_MASTER){
7522 np_index = multi_find_player_by_net_signature(objp->net_signature);
7523 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7524 ignore = &Net_players[np_index];
7528 // if I'm the server, send to all players
7529 if(MULTIPLAYER_MASTER){
7530 multi_io_send_to_all(data, packet_size, ignore);
7532 // otherwise send to the server
7534 multi_io_send(Net_player, data, packet_size);
7538 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7545 offset = HEADER_LENGTH;
7546 GET_DATA_U16( signature );
7547 GET_DATA_S32( rand_val );
7550 objp = multi_get_network_object( signature );
7551 if ( objp == NULL ) {
7552 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7555 if(objp->type != OBJ_SHIP){
7559 // if we're in client firing mode, ignore ones for myself
7560 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7561 if((Player_obj != NULL) && (Player_obj == objp)){
7565 // make it so ship can fire right away!
7566 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7567 if ( objp == Player_obj ){
7568 nprintf(("network", "firing countermeasure from my ship\n"));
7570 ship_launch_countermeasure( objp, rand_val );
7573 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7575 ubyte data[MAX_PACKET_SIZE];
7576 int packet_size = 0;
7580 // only the server should ever be doing this
7581 Assert(MULTIPLAYER_MASTER);
7583 // setup outgoing data
7584 Assert(shooter != NULL);
7585 Assert(turret != NULL);
7586 Assert(target != NULL);
7587 Assert(override != NULL);
7588 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7591 u_beam_info = (ubyte)beam_info_index;
7592 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7593 Assert(subsys_index >= 0);
7594 if(subsys_index < 0){
7599 BUILD_HEADER(BEAM_FIRED);
7600 ADD_DATA_U16(shooter->net_signature);
7601 ADD_DATA(subsys_index);
7602 ADD_DATA_U16(target->net_signature);
7603 ADD_DATA(u_beam_info);
7604 ADD_DATA((*override));
7606 // send to all clients
7607 multi_io_send_to_all_reliable(data, packet_size);
7610 void process_beam_fired_packet(ubyte *data, header *hinfo)
7613 ushort shooter_sig, target_sig;
7617 beam_fire_info fire_info;
7619 // only clients should ever get this
7620 Assert(MULTIPLAYER_CLIENT);
7622 // read in packet data
7623 offset = HEADER_LENGTH;
7624 GET_DATA_U16(shooter_sig);
7625 GET_DATA(subsys_index);
7626 GET_DATA_U16(target_sig);
7627 GET_DATA(u_beam_info);
7629 b_info.dir_a.xyz.x = INTEL_FLOAT( &b_info.dir_a.xyz.x );
7630 b_info.dir_a.xyz.y = INTEL_FLOAT( &b_info.dir_a.xyz.y );
7631 b_info.dir_a.xyz.z = INTEL_FLOAT( &b_info.dir_a.xyz.z );
7632 b_info.dir_b.xyz.x = INTEL_FLOAT( &b_info.dir_b.xyz.x );
7633 b_info.dir_b.xyz.y = INTEL_FLOAT( &b_info.dir_b.xyz.y );
7634 b_info.dir_b.xyz.z = INTEL_FLOAT( &b_info.dir_b.xyz.z );
7635 b_info.delta_ang = INTEL_FLOAT( &b_info.delta_ang );
7636 for (int i=0; i<MAX_BEAM_SHOTS; i++){
7637 b_info.shot_aim[i] = INTEL_FLOAT( &b_info.shot_aim[i] );
7641 // lookup all relevant data
7642 fire_info.beam_info_index = (int)u_beam_info;
7643 fire_info.shooter = NULL;
7644 fire_info.target = NULL;
7645 fire_info.turret = NULL;
7646 fire_info.target_subsys = NULL;
7647 fire_info.beam_info_override = NULL;
7648 fire_info.shooter = multi_get_network_object(shooter_sig);
7649 fire_info.target = multi_get_network_object(target_sig);
7650 fire_info.beam_info_override = &b_info;
7651 fire_info.accuracy = 1.0f;
7652 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)){
7653 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7656 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7657 if(fire_info.turret == NULL){
7658 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7663 beam_fire(&fire_info);
7666 void send_sw_query_packet(ubyte code, char *txt)
7668 ubyte data[MAX_PACKET_SIZE];
7669 int packet_size = 0;
7671 // build the packet and add the code
7672 BUILD_HEADER(SW_STD_QUERY);
7674 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7678 // if I'm the host, send to standalone
7679 if(MULTIPLAYER_HOST){
7680 Assert(!MULTIPLAYER_MASTER);
7681 Assert(code == SW_STD_START);
7682 multi_io_send_reliable(Net_player, data, packet_size);
7684 // otherwise standalone sends back to host
7686 Assert(Game_mode & GM_STANDALONE_SERVER);
7687 Assert(code != SW_STD_START);
7688 Assert(Netgame.host != NULL);
7689 if(Netgame.host != NULL){
7690 multi_io_send_reliable(Netgame.host, data, packet_size);
7695 void process_sw_query_packet(ubyte *data, header *hinfo)
7699 void send_event_update_packet(int event)
7701 ubyte data[MAX_PACKET_SIZE];
7702 ushort u_event = (ushort)event;
7703 int packet_size = 0;
7705 // build the header and add the event
7706 BUILD_HEADER(EVENT_UPDATE);
7707 ADD_DATA_U16(u_event);
7708 ADD_DATA_S32(Mission_events[event].flags);
7709 ADD_DATA_S32(Mission_events[event].formula);
7710 ADD_DATA_S32(Mission_events[event].result);
7711 ADD_DATA_S32(Mission_events[event].count);
7713 // send to all players
7714 multi_io_send_to_all_reliable(data, packet_size);
7717 void process_event_update_packet(ubyte *data, header *hinfo)
7719 int offset = HEADER_LENGTH;
7724 GET_DATA_U16(u_event);
7725 store_flags = Mission_events[u_event].flags;
7726 GET_DATA_S32(Mission_events[u_event].flags);
7727 GET_DATA_S32(Mission_events[u_event].formula);
7728 GET_DATA_S32(Mission_events[u_event].result);
7729 GET_DATA_S32(Mission_events[u_event].count);
7732 // went from non directive special to directive special
7733 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7734 mission_event_set_directive_special(u_event);
7736 // if we went directive special to non directive special
7737 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7738 mission_event_unset_directive_special(u_event);
7742 // weapon detonate packet
7743 void send_weapon_detonate_packet(object *objp)
7745 ubyte data[MAX_PACKET_SIZE];
7746 int packet_size = 0;
7749 Assert(MULTIPLAYER_MASTER);
7750 if(!MULTIPLAYER_MASTER){
7753 Assert(objp != NULL);
7758 // build the header and add the data
7759 BUILD_HEADER(WEAPON_DET);
7760 ADD_DATA_U16(objp->net_signature);
7762 // send to all players
7763 multi_io_send_to_all(data, packet_size);
7766 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7769 int offset = HEADER_LENGTH;
7770 object *objp = NULL;
7772 // get the weapon signature
7773 GET_DATA_U16(net_sig);
7776 // lookup the weapon
7777 objp = multi_get_network_object(net_sig);
7778 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7779 weapon_detonate(objp);
7783 // flak fired packet
7784 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7787 ushort pnet_signature;
7788 ubyte data[MAX_PACKET_SIZE], cindex;
7794 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7798 // local setup -- be sure we are actually passing a weapon!!!!
7799 objp = &Objects[weapon_objnum];
7800 Assert ( objp->type == OBJ_WEAPON );
7801 pnet_signature = Objects[ship_objnum].net_signature;
7803 Assert( subsys_index < UCHAR_MAX );
7804 cindex = (ubyte)subsys_index;
7806 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7811 // build the fire turret packet.
7812 BUILD_HEADER(FLAK_FIRED);
7813 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
7814 ADD_DATA_U16( pnet_signature );
7816 val = (short)ssp->submodel_info_1.angs.h;
7817 ADD_DATA_S16( val );
7818 val = (short)ssp->submodel_info_2.angs.p;
7819 ADD_DATA_S16( val );
7820 ADD_DATA_FL( flak_range );
7822 multi_io_send_to_all(data, packet_size);
7824 multi_rate_add(1, "flk", packet_size);
7827 void process_flak_fired_packet(ubyte *data, header *hinfo)
7829 int offset, weapon_objnum, wid;
7830 ushort pnet_signature;
7838 short pitch, heading;
7841 // get the data for the turret fired packet
7842 offset = HEADER_LENGTH;
7843 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7844 GET_DATA_U16( pnet_signature );
7845 GET_DATA( turret_index );
7846 GET_DATA_S16( heading );
7847 GET_DATA_S16( pitch );
7848 GET_DATA_FL( flak_range );
7849 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7852 objp = multi_get_network_object( pnet_signature );
7853 if ( objp == NULL ) {
7854 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7858 // if this isn't a ship, do nothing
7859 if ( objp->type != OBJ_SHIP ){
7863 // make an orientation matrix from the o_fvec
7864 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7866 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7867 // hack, but should be suitable.
7868 shipp = &Ships[objp->instance];
7869 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7873 wid = ssp->system_info->turret_weapon_type;
7874 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7878 // bash the position and orientation of the turret
7879 ssp->submodel_info_1.angs.h = (float)heading;
7880 ssp->submodel_info_2.angs.p = (float)pitch;
7882 // get the world position of the weapon
7883 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7885 // create the weapon object
7886 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7887 if (weapon_objnum != -1) {
7888 if ( Weapon_info[wid].launch_snd != -1 ) {
7889 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7892 // create a muzzle flash from a flak gun based upon firing position and weapon type
7893 flak_muzzle_flash(&pos, &dir, wid);
7895 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7896 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7900 #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);
7901 #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);
7903 // player pain packet
7904 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7906 ubyte data[MAX_PACKET_SIZE];
7909 int packet_size = 0;
7911 Assert(MULTIPLAYER_MASTER);
7912 if(!MULTIPLAYER_MASTER){
7920 // build the packet and add the code
7921 BUILD_HEADER(NETPLAYER_PAIN);
7922 windex = (ubyte)weapon_info_index;
7924 udamage = (ushort)damage;
7925 ADD_DATA_U16(udamage);
7926 //ADD_DATA((*force));
7927 add_vector_data( data, &packet_size, *force );
7928 //ADD_DATA((*hitpos));
7929 add_vector_data( data, &packet_size, *hitpos );
7931 // send to the player
7932 multi_io_send(pl, data, packet_size);
7934 multi_rate_add(1, "pai", packet_size);
7937 void process_player_pain_packet(ubyte *data, header *hinfo)
7943 vector local_hit_pos;
7946 // get the data for the pain packet
7947 offset = HEADER_LENGTH;
7949 GET_DATA_U16(udamage);
7951 get_vector_data( data, &offset, force );
7952 //GET_DATA(local_hit_pos);
7953 get_vector_data( data, &offset, local_hit_pos );
7956 mprintf(("PAIN!\n"));
7958 // get weapon info pointer
7959 //Assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)); // always true
7960 if(! ((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
7963 wip = &Weapon_info[windex];
7965 // play the weapon hit sound
7966 Assert(Player_obj != NULL);
7967 if(Player_obj == NULL){
7970 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
7972 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
7973 ship_hit_pain((float)udamage);
7976 ship_apply_whack(&force, &local_hit_pos, Player_obj);
7980 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
7982 ubyte data[MAX_PACKET_SIZE];
7984 int packet_size = 0;
7986 // build the header and add the data
7987 BUILD_HEADER(LIGHTNING_PACKET);
7988 val = (char)bolt_type;
7990 //ADD_DATA((*start));
7991 add_vector_data( data, &packet_size, *start );
7992 //ADD_DATA((*strike));
7993 add_vector_data( data, &packet_size, *strike );
7995 // send to everyone unreliable for now
7996 multi_io_send_to_all(data, packet_size);
7999 void process_lightning_packet(ubyte *data, header *hinfo)
8003 vector start, strike;
8006 offset = HEADER_LENGTH;
8007 GET_DATA(bolt_type);
8009 get_vector_data(data, &offset, start);
8010 // GET_DATA(strike);
8011 get_vector_data(data, &offset, strike);
8020 nebl_bolt(bolt_type, &start, &strike);
8023 void send_bytes_recvd_packet(net_player *pl)
8025 // only clients should ever be doing this
8030 ubyte data[MAX_PACKET_SIZE];
8031 int packet_size = 0;
8032 BUILD_HEADER(BYTES_SENT);
8033 ADD_DATA_S32(pl->cl_bytes_recvd);
8035 // send to the server
8036 multi_io_send_reliable(pl, data, packet_size);
8039 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
8043 net_player *pl = NULL;
8044 int offset = HEADER_LENGTH;
8046 GET_DATA_S32(bytes);
8050 if(Net_player == NULL){
8053 if(!MULTIPLAYER_MASTER){
8057 // make sure we know what player sent this
8058 pid = find_player_id(hinfo->id);
8059 if((pid < 0) || (pid >= MAX_PLAYERS)){
8062 pl = &Net_players[pid];
8065 pl->cl_bytes_recvd = bytes;
8069 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
8072 pl->sv_bytes_sent = 0;
8076 void send_host_captain_change_packet(short player_id, int captain_change)
8078 ubyte data[MAX_PACKET_SIZE];
8079 int packet_size = 0;
8082 BUILD_HEADER(TRANSFER_HOST);
8083 ADD_DATA_S16(player_id);
8084 ADD_DATA_S32(captain_change);
8087 multi_io_send_to_all_reliable(data, packet_size);
8090 void process_host_captain_change_packet(ubyte *data, header *hinfo)
8092 int offset = HEADER_LENGTH;
8093 int idx, found_player, captain_change;
8096 // get the player id
8097 GET_DATA_S16(player_id);
8098 GET_DATA_S32(captain_change);
8104 for(idx=0; idx<MAX_PLAYERS; idx++){
8105 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8106 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
8111 // unflag all old players
8112 for(idx=0; idx<MAX_PLAYERS; idx++){
8113 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
8118 for(idx=0; idx<MAX_PLAYERS; idx++){
8119 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8120 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
8122 // spew to the HUD config
8123 if(Net_players[idx].player != NULL){
8124 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
8134 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
8139 void send_self_destruct_packet()
8141 ubyte data[MAX_PACKET_SIZE];
8142 int packet_size = 0;
8145 if(Net_player == NULL){
8149 // if i'm the server, I shouldn't be here
8150 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8151 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8155 // only if this is valid
8156 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8161 if((Player_ship == NULL) || (Player_obj == NULL)){
8166 BUILD_HEADER(SELF_DESTRUCT);
8167 ADD_DATA_U16(Player_obj->net_signature);
8169 // send to the server
8170 multi_io_send_reliable(Net_player, data, packet_size);
8173 void process_self_destruct_packet(ubyte *data, header *hinfo)
8175 int offset = HEADER_LENGTH;
8179 // get the net signature
8180 GET_DATA_U16(net_sig);
8184 np_index = find_player_id(hinfo->id);
8188 if(MULTI_OBSERVER(Net_players[np_index])){
8191 if(Net_players[np_index].player == NULL){
8194 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8197 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8200 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8203 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8208 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);