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.5 2002/06/17 06:33:10 relnev
19 * ryan's struct patch for gcc 2.95
21 * Revision 1.4 2002/06/09 04:41:24 relnev
22 * added copyright header
24 * Revision 1.3 2002/05/26 20:49:54 theoddone33
27 * Revision 1.2 2002/05/07 03:16:47 theoddone33
28 * The Great Newline Fix
30 * Revision 1.1.1.1 2002/05/03 03:28:10 root
34 * 83 9/14/99 2:21p Dave
35 * Fixed observer mode joining and ingame stuff.
37 * 82 9/14/99 3:26a Dave
38 * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
39 * respawn-too-early problem. Made a few crash points safe.
41 * 81 9/13/99 4:52p Dave
44 * 80 9/08/99 10:01p Dave
45 * Make sure game won't run in a drive's root directory. Make sure
46 * standalone routes suqad war messages properly to the host.
48 * 79 8/28/99 4:54p Dave
49 * Fixed directives display for multiplayer clients for wings with
50 * multiple waves. Fixed hud threat indicator rendering color.
52 * 78 8/27/99 12:32a Dave
53 * Allow the user to specify a local port through the launcher.
55 * 77 8/26/99 8:51p Dave
56 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
58 * 76 8/25/99 4:38p Dave
59 * Updated PXO stuff. Make squad war report stuff much more nicely.
61 * 75 8/24/99 1:50a Dave
62 * Fixed client-side afterburner stuttering. Added checkbox for no version
63 * checking on PXO join. Made button info passing more friendly between
66 * 74 8/22/99 5:53p Dave
67 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
68 * instead of ship designations for multiplayer players.
70 * 73 8/22/99 1:55p Dave
71 * Cleaned up host/team-captain leaving code.
73 * 72 8/22/99 1:19p Dave
74 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
75 * which d3d cards are detected.
77 * 71 8/19/99 10:59a Dave
78 * Packet loss detection.
80 * 70 8/17/99 1:12p Dave
81 * Send TvT update when a client has finished joining so he stays nice and
84 * 69 8/16/99 4:05p Dave
85 * Big honking checkin.
87 * 68 8/11/99 5:54p Dave
88 * Fixed collision problem. Fixed standalone ghost problem.
90 * 67 8/06/99 9:46p Dave
91 * Hopefully final changes for the demo.
93 * 66 8/05/99 2:06a Dave
96 * 65 7/30/99 7:01p Dave
97 * Dogfight escort gauge. Fixed up laser rendering in Glide.
99 * 64 7/29/99 5:41p Jefff
100 * Sound hooks for cmeasure success
102 * 63 7/28/99 5:34p Dave
103 * Nailed the missing stats bug to the wall. Problem was optimized build
104 * and using GET_DATA() with array elements. BLECH.
106 * 62 7/26/99 5:50p Dave
107 * Revised ingame join. Better? We'll see....
109 * 61 7/24/99 1:54p Dave
110 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
113 * 60 7/22/99 7:17p Dave
114 * Fixed excessive whacks in multiplayer.
116 * 59 7/08/99 10:53a Dave
117 * New multiplayer interpolation scheme. Not 100% done yet, but still
118 * better than the old way.
120 * 58 7/03/99 5:50p Dave
121 * Make rotated bitmaps draw properly in padlock views.
123 * 57 7/03/99 4:08p Dave
124 * Fixed wss_slots size issues. Fixed potentially nasty bug in low level
127 * 56 6/21/99 7:24p Dave
128 * netplayer pain packet. Added type E unmoving beams.
130 * 55 6/18/99 5:16p Dave
131 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
132 * dialog to PXO screen.
134 * 54 6/16/99 4:06p Dave
135 * New pilot info popup. Added new draw-bitmap-as-poly function.
137 * 53 6/16/99 10:20a Dave
138 * Added send-message-list sexpression.
140 * 52 6/04/99 3:52p Anoop
141 * Removed bogus assert.
143 * 51 6/01/99 8:35p Dave
144 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
145 * awacs-set-radius sexpression.
147 * 50 5/21/99 5:03p Andsager
148 * Add code to display engine wash death. Modify ship_kill_packet
150 * 49 5/18/99 1:30p Dave
151 * Added muzzle flash table stuff.
153 * 48 5/14/99 1:59p Andsager
154 * Multiplayer message for subsystem cargo revealed.
156 * 47 5/14/99 12:15p Andsager
157 * Add vaporized to kill packet
159 * 46 5/03/99 8:32p Dave
160 * New version of multi host options screen.
162 * 45 4/30/99 12:18p Dave
163 * Several minor bug fixes.
165 * 44 4/29/99 2:29p Dave
166 * Made flak work much better in multiplayer.
168 * 43 4/28/99 11:13p Dave
169 * Temporary checkin of artillery code.
171 * 42 4/16/99 5:54p Dave
172 * Support for on/off style "stream" weapons. Real early support for
173 * target-painting lasers.
175 * 41 4/12/99 2:22p Dave
176 * More checks for dogfight stats.
178 * 40 4/09/99 2:21p Dave
179 * Multiplayer beta stuff. CD checking.
181 * 39 4/02/99 9:55a Dave
182 * Added a few more options in the weapons.tbl for beam weapons. Attempt
183 * at putting "pain" packets into multiplayer.
185 * 38 4/01/99 3:41p Anoop
186 * Removed bogus Int3().
188 * 37 3/19/99 9:51a Dave
189 * Checkin to repair massive source safe crash. Also added support for
190 * pof-style nebulae, and some new weapons code.
192 * 38 3/12/99 2:32p Anoop
193 * Removed bogus asserts.
195 * 37 3/11/99 11:41a Neilk
196 * Don't do multi_io_* operations in single-player
198 * 36 3/10/99 6:50p Dave
199 * Changed the way we buffer packets for all clients. Optimized turret
200 * fired packets. Did some weapon firing optimizations.
202 * 35 3/09/99 6:24p Dave
203 * More work on object update revamping. Identified several sources of
204 * unnecessary bandwidth.
206 * 34 3/08/99 7:03p Dave
207 * First run of new object update system. Looks very promising.
209 * 33 3/04/99 6:09p Dave
210 * Added in sexpressions for firing beams and checking for if a ship is
213 * 32 3/01/99 10:00a Dave
214 * Fxied several dogfight related stats bugs.
216 * 31 2/24/99 2:25p Dave
217 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
218 * bug for dogfight more.
220 * 30 2/23/99 2:29p Dave
221 * First run of oldschool dogfight mode.
223 * 29 2/21/99 6:01p Dave
224 * Fixed standalone WSS packets.
226 * 28 2/21/99 1:48p Dave
227 * Some code for monitoring datarate for multiplayer in detail.
229 * 27 2/17/99 2:11p Dave
230 * First full run of squad war. All freespace and tracker side stuff
233 * 26 2/12/99 6:16p Dave
234 * Pre-mission Squad War code is 95% done.
236 * 25 2/11/99 3:08p Dave
237 * PXO refresh button. Very preliminary squad war support.
239 * 24 1/29/99 5:07p Dave
240 * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
243 * 23 1/27/99 9:56a Dave
244 * Temporary checkin of beam weapons for Dan to make cool sounds.
246 * 22 1/26/99 6:33p Anoop
247 * Fixed multiplayer slot switching problem (be sure to remember that
248 * hinfo->id is player id# _not_ player index #)
250 * 21 1/24/99 11:37p Dave
251 * First full rev of beam weapons. Very customizable. Removed some bogus
252 * Int3()'s in low level net code.
254 * 20 1/15/99 4:37p Dave
255 * Potential fix for weapon pair problem.
257 * 19 1/14/99 6:06p Dave
258 * 100% full squad logo support for single player and multiplayer.
260 * 18 1/14/99 12:48a Dave
261 * Todo list bug fixes. Made a pass at putting briefing icons back into
262 * FRED. Sort of works :(
264 * 17 1/12/99 5:45p Dave
265 * Moved weapon pipeline in multiplayer to almost exclusively client side.
266 * Very good results. Bandwidth goes down, playability goes up for crappy
267 * connections. Fixed object update problem for ship subsystems.
269 * 16 1/08/99 4:56p Anoop
270 * Fixed a problem with wss request packets.
272 * 15 12/18/98 12:24p Markm
273 * Fixed a dumb bug where player image_filenames were not being passed
274 * properly in new players packet.
276 * 14 12/14/98 12:13p Dave
277 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
280 * 13 11/30/98 1:07p Dave
281 * 16 bit conversion, first run.
283 * 12 11/20/98 4:08p Dave
284 * Fixed flak effect in multiplayer.
286 * 11 11/19/98 4:19p Dave
287 * Put IPX sockets back in psnet. Consolidated all multiplayer config
290 * 10 11/19/98 8:04a Dave
291 * Full support for D3-style reliable sockets. Revamped packet lag/loss
292 * system, made it receiver side and at the lowest possible level.
294 * 9 11/17/98 11:12a Dave
295 * Removed player identification by address. Now assign explicit id #'s.
297 * 8 11/12/98 11:50a Dave
298 * Multiplayer clients set flak range to be very long.
300 * 7 11/12/98 12:13a Dave
301 * Tidied code up for multiplayer test. Put in network support for flak
304 * 6 11/05/98 5:55p Dave
305 * Big pass at reducing #includes
307 * 5 10/20/98 1:39p Andsager
308 * Make so sparks follow animated ship submodels. Modify
309 * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
310 * submodel_num. Add submodel_num to multiplayer hit packet.
312 * 4 10/13/98 9:29a Dave
313 * Started neatening up freespace.h. Many variables renamed and
314 * reorganized. Added AlphaColors.[h,cpp]
316 * 3 10/07/98 6:27p Dave
317 * Globalized mission and campaign file extensions. Removed Silent Threat
318 * special code. Moved \cache \players and \multidata into the \data
321 * 2 10/07/98 10:53a Dave
324 * 1 10/07/98 10:50a Dave
326 * 506 10/02/98 3:22p Allender
327 * fix up the -connect option and fix the -port option
329 * 505 10/02/98 11:45a Dave
330 * Fixed stupid chat message bug.
332 * 504 9/29/98 1:33p Dave
333 * Remove standalone only conditional compiles for pre 1.04 stuff.
335 * 503 9/28/98 1:54p Dave
336 * Make sure French and German don't xfer builtin files they don't have
339 * 502 9/20/98 7:19p Dave
340 * Added CHANGE_IFF packet.
342 * 501 9/17/98 3:08p Dave
343 * PXO to non-pxo game warning popup. Player icon stuff in create and join
344 * game screens. Upped server count refresh time in PXO to 35 secs (from
353 #include <io.h> // for findfirst/findnext, etc
356 #include "multimsgs.h"
357 #include "multiutil.h"
360 #include "multiteamselect.h"
361 #include "linklist.h"
362 #include "gamesequence.h"
363 #include "hudmessage.h"
364 #include "hudsquadmsg.h"
365 #include "freespace.h"
369 #include "missiongoals.h"
370 #include "missionparse.h"
371 #include "missionlog.h"
372 #include "missionmessage.h"
373 #include "missionbrief.h"
375 #include "cmeasure.h"
376 #include "model.h" // for some limits
377 #include "afterburner.h"
378 #include "stand_gui.h"
379 #include "multi_xfer.h"
385 #include "managepilot.h"
386 #include "hudsquadmsg.h"
388 #include "missionweaponchoice.h"
389 #include "missionshipchoice.h"
390 #include "fireballs.h"
393 #include "multi_ingame.h"
394 #include "multiteamselect.h"
396 #include "multi_campaign.h"
397 #include "multi_team.h"
398 #include "multi_respawn.h"
399 #include "multi_observer.h"
400 #include "multi_voice.h"
401 #include "asteroid.h"
402 #include "multi_pmsg.h"
403 #include "multi_data.h"
404 #include "multi_options.h"
405 #include "objcollide.h"
406 #include "hudreticle.h"
407 #include "multi_pause.h"
408 #include "multi_endgame.h"
409 #include "missiondebrief.h"
410 #include "multi_obj.h"
411 #include "multi_log.h"
413 #include "multi_kick.h"
417 #include "multi_rate.h"
418 #include "neblightning.h"
419 #include "hudescort.h"
421 // #define _MULTI_SUPER_WACKY_COMPRESSION
423 #ifdef _MULTI_SUPER_WACKY_COMPRESSION
425 #define MAX_CODE ( ( 1 << BITS ) - 1 )
426 #define TABLE_SIZE 35023L
427 #define END_OF_STREAM 256
428 #define BUMP_CODE 257
429 #define FLUSH_CODE 258
430 #define FIRST_CODE 259
439 static DICTIONARY dict[TABLE_SIZE];
440 static char decode_stack[TABLE_SIZE];
441 static uint next_code;
442 static int current_code_bits;
443 static uint next_bump_code;
445 typedef struct BitBuf {
451 void output_bits( BitBuf *bitbuf, uint code, int count )
455 mask = 1L << ( count - 1 );
458 bitbuf->rack |= bitbuf->mask;
460 if ( bitbuf->mask == 0 ) {
461 *bitbuf->data++=(ubyte)bitbuf->rack;
469 uint input_bits( BitBuf *bitbuf, int bit_count )
474 mask = 1L << ( bit_count - 1 );
477 if ( bitbuf->mask == 0x80 ) {
478 bitbuf->rack = *bitbuf->data++;
479 if ( bitbuf->rack == EOF )
480 return END_OF_STREAM;
482 if ( bitbuf->rack & bitbuf->mask )
483 return_value |= mask;
486 if ( bitbuf->mask == 0 )
489 return( return_value );
493 static void InitializeDictionary()
497 for ( i = 0 ; i < TABLE_SIZE ; i++ )
498 dict[i].code_value = UNUSED;
500 next_code = FIRST_CODE;
501 current_code_bits = 9;
502 next_bump_code = 511;
506 static uint find_child_node( int parent_code, int child_character )
511 index = ( child_character << ( BITS - 8 ) ) ^ parent_code;
515 offset = TABLE_SIZE - index;
517 if ( dict[ index ].code_value == UNUSED )
518 return( (uint) index );
519 if ( dict[ index ].parent_code == parent_code &&
520 dict[ index ].character == (char) child_character )
522 if ( (int) index >= offset )
525 index += TABLE_SIZE - offset;
530 static uint decode_string( uint count, uint code )
532 while ( code > 255 ) {
533 decode_stack[ count++ ] = dict[ code ].character;
534 code = dict[ code ].parent_code;
536 decode_stack[ count++ ] = (char) code;
540 int lzw_compress( ubyte *outputbuf, ubyte *inputbuf, int input_size )
548 // Init output bit buffer
551 output.data = outputbuf;
553 InitializeDictionary();
555 string_code = *inputbuf++;
557 for ( i=1 ; i<input_size ; i++ ) {
558 character = *inputbuf++;
559 index = find_child_node( string_code, character );
560 if ( dict[ index ].code_value != - 1 )
561 string_code = dict[ index ].code_value;
563 dict[ index ].code_value = next_code++;
564 dict[ index ].parent_code = string_code;
565 dict[ index ].character = (char) character;
566 output_bits( &output, (unsigned long) string_code, current_code_bits );
567 string_code = character;
568 if ( next_code > MAX_CODE ) {
569 output_bits( &output, (unsigned long) FLUSH_CODE, current_code_bits );
570 InitializeDictionary();
571 } else if ( next_code > next_bump_code ) {
572 output_bits( &output, (unsigned long) BUMP_CODE, current_code_bits );
574 next_bump_code <<= 1;
579 output_bits( &output, (unsigned long) string_code, current_code_bits );
580 output_bits( &output, (unsigned long) END_OF_STREAM, current_code_bits);
582 if ( output.mask != 0x80 )
583 *output.data++ = (ubyte)output.rack;
585 return output.data-outputbuf;
589 int lzw_expand( ubyte *outputbuf, ubyte *inputbuf )
600 input.data = inputbuf;
604 InitializeDictionary();
605 old_code = (uint) input_bits( &input, current_code_bits );
606 if ( old_code == END_OF_STREAM )
608 character = old_code;
609 outputbuf[counter++] = ( ubyte )old_code;
611 new_code = (uint) input_bits( &input, current_code_bits );
612 if ( new_code == END_OF_STREAM )
614 if ( new_code == FLUSH_CODE )
616 if ( new_code == BUMP_CODE ) {
620 if ( new_code >= next_code ) {
621 decode_stack[ 0 ] = (char) character;
622 count = decode_string( 1, old_code );
624 count = decode_string( 0, new_code );
626 character = decode_stack[ count - 1 ];
628 outputbuf[counter++] = ( ubyte )decode_stack[ --count ];
629 dict[ next_code ].parent_code = old_code;
630 dict[ next_code ].character = (char) character;
638 // send the specified data packet to all players
639 void multi_io_send(net_player *pl, ubyte *data, int len)
642 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
646 // don't do it for single player
647 if(!(Game_mode & GM_MULTIPLAYER)){
652 if(MULTIPLAYER_CLIENT){
653 // Assert(pl == Net_player);
654 if(pl != Net_player){
658 // Assert(pl != Net_player);
659 if(pl == Net_player){
664 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
665 if ((pl->s_info.unreliable_buffer_size + len) > MAX_PACKET_SIZE) {
666 multi_io_send_force(pl);
667 pl->s_info.unreliable_buffer_size = 0;
670 Assert((pl->s_info.unreliable_buffer_size + len) <= MAX_PACKET_SIZE);
672 memcpy(pl->s_info.unreliable_buffer + pl->s_info.unreliable_buffer_size, data, len);
673 pl->s_info.unreliable_buffer_size += len;
676 void multi_io_send_to_all(ubyte *data, int length, net_player *ignore)
679 Assert(MULTIPLAYER_MASTER);
681 // need to check for i > 1, hmmm... and connected. I don't know.
682 for (i = 0; i < MAX_PLAYERS; i++ ) {
683 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
687 // maybe ignore a player
688 if((ignore != NULL) && (&Net_players[i] == ignore)){
692 // ingame joiners not waiting to select a ship doesn't get any packets
693 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
698 multi_io_send(&Net_players[i], data, length);
702 void multi_io_send_force(net_player *pl)
705 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
709 // don't do it for single player
710 if(!(Game_mode & GM_MULTIPLAYER)){
714 // send everything in
715 if (MULTIPLAYER_MASTER) {
716 psnet_send(&pl->p_info.addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
718 // add the bytes sent to this player
719 pl->sv_bytes_sent += pl->s_info.unreliable_buffer_size;
721 psnet_send(&Netgame.server_addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
723 pl->s_info.unreliable_buffer_size = 0;
726 // send the data packet to all players via their reliable sockets
727 void multi_io_send_reliable(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.reliable_buffer_size + len) > MAX_PACKET_SIZE) {
754 multi_io_send_reliable_force(pl);
755 pl->s_info.reliable_buffer_size = 0;
758 Assert((pl->s_info.reliable_buffer_size + len) <= MAX_PACKET_SIZE);
760 memcpy(pl->s_info.reliable_buffer + pl->s_info.reliable_buffer_size, data, len);
761 pl->s_info.reliable_buffer_size += len;
764 void multi_io_send_to_all_reliable(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_reliable(&Net_players[i], data, length);
790 void multi_io_send_reliable_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_rel_send(pl->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
805 } else if(Net_player != NULL){
806 psnet_rel_send(Net_player->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
808 pl->s_info.reliable_buffer_size = 0;
811 // send all buffered packets
812 void multi_io_send_buffered_packets()
816 // don't do it for single player
817 if(!(Game_mode & GM_MULTIPLAYER)){
822 if(MULTIPLAYER_MASTER){
823 for(idx=0; idx<MAX_PLAYERS; idx++){
824 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
825 // force unreliable data
826 if(Net_players[idx].s_info.unreliable_buffer_size > 0){
827 multi_io_send_force(&Net_players[idx]);
828 Net_players[idx].s_info.unreliable_buffer_size = 0;
831 // force reliable data
832 if(Net_players[idx].s_info.reliable_buffer_size > 0){
833 multi_io_send_reliable_force(&Net_players[idx]);
834 Net_players[idx].s_info.reliable_buffer_size = 0;
840 else if(Net_player != NULL){
841 // force unreliable data
842 if(Net_player->s_info.unreliable_buffer_size > 0){
843 multi_io_send_force(Net_player);
844 Net_player->s_info.unreliable_buffer_size = 0;
847 // force reliable data
848 if(Net_player->s_info.reliable_buffer_size > 0){
849 multi_io_send_reliable_force(Net_player);
850 Net_player->s_info.reliable_buffer_size = 0;
855 // 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)
856 void send_game_chat_packet(net_player *from, char *msg, int msg_mode, net_player *to, char *expr, int server_msg)
858 ubyte data[MAX_PACKET_SIZE],mode;
861 BUILD_HEADER(GAME_CHAT);
864 ADD_DATA(from->player_id);
866 // add the message mode and if in MSG_TARGET mode, add who the target is
867 ADD_DATA(server_msg);
868 mode = (ubyte)msg_mode;
871 case MULTI_MSG_TARGET:
873 ADD_DATA(to->player_id);
876 Assert(expr != NULL);
880 // add the message itself
883 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
885 // message all players
887 for(idx=0;idx<MAX_PLAYERS;idx++){
888 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from)){
889 multi_io_send_reliable(&Net_players[idx], data, packet_size);
894 // message only friendly players
895 case MULTI_MSG_FRIENDLY:
896 for(idx=0;idx<MAX_PLAYERS;idx++){
897 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)){
898 multi_io_send_reliable(&Net_players[idx], data, packet_size);
903 // message only hostile players
904 case MULTI_MSG_HOSTILE:
905 for(idx=0;idx<MAX_PLAYERS;idx++){
906 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)){
907 multi_io_send_reliable(&Net_players[idx], data, packet_size);
912 // message the player's target
913 case MULTI_MSG_TARGET:
915 if(MULTI_CONNECTED((*to)) && !MULTI_STANDALONE((*to))){
916 multi_io_send_reliable(to, data, packet_size);
920 // message all players who match the expression string
922 Assert(expr != NULL);
923 for(idx=0;idx<MAX_PLAYERS;idx++){
924 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && multi_msg_matches_expr(&Net_players[idx],expr) ){
925 multi_io_send_reliable(&Net_players[idx], data, packet_size);
931 // send to the server, who will take care of routing it
933 multi_io_send_reliable(Net_player, data, packet_size);
937 // process a general game chat packet, if we're the standalone we should rebroadcast
938 void process_game_chat_packet( ubyte *data, header *hinfo )
942 int color_index,player_index,to_player_index,should_display,server_msg;
943 char msg[MULTI_MSG_MAX_TEXT_LEN+CALLSIGN_LEN+2];
947 offset = HEADER_LENGTH;
949 // get the id of the sender
952 // determine if this is a server message
953 GET_DATA(server_msg);
958 // if targeting a specific player, get the address
961 case MULTI_MSG_TARGET:
968 // get the message itself
972 // get the index of the sending player
973 color_index = find_player_id(from);
974 player_index = color_index;
976 // if we couldn't find the player - bail
977 if(player_index == -1){
978 nprintf(("Network","Could not find player for processing game chat packet!\n"));
984 // if we're the server, determine what to do with the packet here
985 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
986 // if he's targeting a specific player, find out who it is
987 if(mode == MULTI_MSG_TARGET){
988 to_player_index = find_player_id(to);
990 to_player_index = -1;
993 // if we couldn't find who sent the message or who should be getting the message, the bail
994 if(((to_player_index == -1) && (mode == MULTI_MSG_TARGET)) || (player_index == -1)){
998 // determine if _I_ should be seeing the text
999 if(Game_mode & GM_STANDALONE_SERVER){
1002 // check against myself for several specific cases
1004 if((mode == MULTI_MSG_ALL) ||
1005 ((mode == MULTI_MSG_FRIENDLY) && (Net_player->p_info.team == Net_players[player_index].p_info.team)) ||
1006 ((mode == MULTI_MSG_HOSTILE) && (Net_player->p_info.team != Net_players[player_index].p_info.team)) ||
1007 ((mode == MULTI_MSG_TARGET) && (MY_NET_PLAYER_NUM == to_player_index)) ||
1008 ((mode == MULTI_MSG_EXPR) && multi_msg_matches_expr(Net_player,expr)) ){
1013 // if we're the server of a game, we need to rebroadcast to all other players
1015 // individual target mission
1016 case MULTI_MSG_TARGET:
1017 // if I was the inteneded target, or we couldn't find the intended target, don't rebroadcast
1018 if(to_player_index != MY_NET_PLAYER_NUM){
1019 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, &Net_players[to_player_index], NULL, server_msg);
1023 case MULTI_MSG_EXPR:
1024 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, expr, server_msg);
1028 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, NULL, server_msg);
1032 // if a client receives this packet, its always ok for him to display it
1037 // if we're not on a standalone
1039 if(server_msg == 2){
1042 multi_display_chat_msg(msg, player_index, !server_msg);
1047 // broadcast a hud message to all players
1048 void send_hud_msg_to_all( char* msg )
1050 ubyte data[MAX_PACKET_SIZE];
1053 // only the server should be sending this packet
1054 BUILD_HEADER(HUD_MSG);
1058 multi_io_send_to_all( data, packet_size );
1061 // process an incoming hud message packet
1062 void process_hud_message(ubyte* data, header* hinfo)
1065 char msg_buffer[255];
1067 offset = HEADER_LENGTH;
1069 GET_STRING(msg_buffer);
1072 // this is the only safe place to do this since only in the mission is the HUD guaranteed to be inited
1073 if(Game_mode & GM_IN_MISSION){
1074 HUD_printf(msg_buffer);
1078 // send a join packet request to the specified address (should be a server)
1079 void send_join_packet(net_addr* addr,join_request *jr)
1081 ubyte data[MAX_PACKET_SIZE];
1084 // build the header and add the request
1088 psnet_send(addr, data, packet_size);
1091 // process an incoming join request packet
1092 void process_join_packet(ubyte* data, header* hinfo)
1097 int host_restr_mode;
1098 // int team0_avail,team1_avail;
1099 char join_string[255];
1102 // only the server of the game should ever receive this packet
1103 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
1106 offset = HEADER_LENGTH;
1108 // read in the request info
1109 memset(&jr,0,sizeof(join_request));
1115 // fill in the address information of where this came from
1116 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
1118 // determine if we should accept this guy, or return a reason we should reject him
1119 // see the DENY_* codes in multi.h
1120 ret_code = multi_eval_join_request(&jr,&addr);
1122 // evaluate the return code
1124 // he should be accepted
1128 // we have to query the host because this is a restricted game
1129 case JOIN_QUERY_RESTRICTED :
1130 if(!(Game_mode & GM_STANDALONE_SERVER)){
1131 // notify the host of the event
1132 snd_play(&Snds[SND_CUE_VOICE]);
1135 // set the query timestamp
1136 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
1137 Netgame.flags |= NG_FLAG_INGAME_JOINING;
1139 // determine what mode we're in
1140 host_restr_mode = -1;
1141 memset(join_string,0,255);
1142 // if(Netgame.type == NG_TYPE_TEAM){
1143 // multi_player_ships_available(&team0_avail,&team1_avail);
1145 // if(team0_avail && team1_avail){
1146 // host_restr_mode = MULTI_JOIN_RESTR_MODE_4;
1147 // sprintf(join_string,"Player %s has tried to join. Accept on team 1 or 2 ?",jr.callsign);
1148 // } else if(team0_avail && !team1_avail){
1149 // host_restr_mode = MULTI_JOIN_RESTR_MODE_2;
1150 // sprintf(join_string,"Player %s has tried to join team 0, accept y/n ? ?",jr.callsign);
1151 // } else if(!team0_avail && team1_avail){
1152 // host_restr_mode = MULTI_JOIN_RESTR_MODE_3;
1153 // sprintf(join_string,"Player %s has tried to join team 1, accept y/n ?",jr.callsign);
1155 // } else if(Netgame.mode == NG_MODE_RESTRICTED){
1156 host_restr_mode = MULTI_JOIN_RESTR_MODE_1;
1157 sprintf(join_string,XSTR("Player %s has tried to join, accept y/n ?",715),jr.callsign);
1159 Assert(host_restr_mode != -1);
1161 // store the request info
1162 memcpy(&Multi_restr_join_request,&jr,sizeof(join_request));
1163 memcpy(&Multi_restr_addr,&addr,sizeof(net_addr));
1164 Multi_join_restr_mode = host_restr_mode;
1166 // if i'm the standalone server, I need to send a query to the host
1167 if(Game_mode & GM_STANDALONE_SERVER){
1168 send_host_restr_packet(jr.callsign,0,Multi_join_restr_mode);
1170 HUD_printf(join_string);
1174 ml_printf(NOX("Receive restricted join request from %s"), jr.callsign);
1178 // he'e being denied for some reason
1180 // send him the reason he is being denied
1181 send_deny_packet(&addr,ret_code);
1185 // process the rest of the request
1186 multi_process_valid_join_request(&jr,&addr);
1189 // send a notification that a new player has joined the game (if target != NULL, broadcast the packet)
1190 void send_new_player_packet(int new_player_num,net_player *target)
1192 ubyte data[MAX_PACKET_SIZE], val;
1193 int packet_size = 0;
1195 BUILD_HEADER( NOTIFY_NEW_PLAYER );
1197 // add the new player's info
1198 ADD_DATA(new_player_num);
1199 ADD_DATA(Net_players[new_player_num].p_info.addr);
1200 ADD_DATA(Net_players[new_player_num].player_id);
1201 ADD_DATA(Net_players[new_player_num].flags);
1202 ADD_STRING(Net_players[new_player_num].player->callsign);
1203 ADD_STRING(Net_players[new_player_num].player->image_filename);
1204 ADD_STRING(Net_players[new_player_num].player->squad_filename);
1205 ADD_STRING(Net_players[new_player_num].p_info.pxo_squad_name);
1207 val = (ubyte)Net_players[new_player_num].p_info.team;
1210 // broadcast the data
1212 multi_io_send_reliable(target, data, packet_size);
1214 multi_io_send_to_all_reliable(data, packet_size);
1218 // process a notification for a new player who has joined the game
1219 void process_new_player_packet(ubyte* data, header* hinfo)
1221 int already_in_game = 0;
1222 int offset, new_player_num,player_num,new_flags;
1224 char new_player_name[CALLSIGN_LEN+2] = "";
1225 char new_player_image[MAX_FILENAME_LEN+1] = "";
1226 char new_player_squad[MAX_FILENAME_LEN+1] = "";
1227 char new_player_pxo_squad[LOGIN_LEN+1] = "";
1228 char notify_string[256];
1232 offset = HEADER_LENGTH;
1234 // get the new players information
1235 GET_DATA(new_player_num);
1238 GET_DATA(new_flags);
1239 GET_STRING(new_player_name);
1240 GET_STRING(new_player_image);
1241 GET_STRING(new_player_squad);
1242 GET_STRING(new_player_pxo_squad);
1246 player_num = multi_find_open_player_slot();
1247 Assert(player_num != -1);
1249 // note that this new code does not check for duplicate IPs. It merely checks to see if
1250 // the slot referenced by new_player_num is already occupied by a connected player
1251 if(MULTI_CONNECTED(Net_players[new_player_num])){
1255 // if he's not alreayd in the game for one reason or another
1256 if ( !already_in_game ) {
1257 if ( Game_mode & GM_IN_MISSION ){
1258 HUD_sourced_printf(HUD_SOURCE_COMPUTER, XSTR("%s has entered the game\n",716), new_player_name);
1261 // create the player
1262 memcpy(new_addr.net_id, Psnet_my_addr.net_id, 4);
1264 if(new_flags & NETINFO_FLAG_OBSERVER){
1265 multi_obs_create_player(new_player_num,new_player_name,&new_addr,&Players[player_num]);
1266 Net_players[new_player_num].flags |= new_flags;
1268 multi_create_player( new_player_num, &Players[player_num],new_player_name, &new_addr, -1, new_id );
1269 Net_players[new_player_num].flags |= new_flags;
1272 // copy in the filename
1273 if(strlen(new_player_image) > 0){
1274 strcpy(Net_players[new_player_num].player->image_filename, new_player_image);
1276 strcpy(Net_players[new_player_num].player->image_filename, "");
1278 // copy his pilot squad filename
1279 Net_players[new_player_num].player->insignia_texture = -1;
1280 player_set_squad_bitmap(Net_players[new_player_num].player, new_player_squad);
1282 // copy in his pxo squad name
1283 strcpy(Net_players[new_player_num].p_info.pxo_squad_name, new_player_pxo_squad);
1285 // since we just created the player, set the last_heard_time here.
1286 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1288 Net_players[new_player_num].p_info.team = team;
1290 Net_players[new_player_num].player_id = new_id;
1292 // zero out this players ping
1293 multi_ping_reset(&Net_players[new_player_num].s_info.ping);
1295 // add a chat message
1296 if(Net_players[new_player_num].player->callsign != NULL){
1297 sprintf(notify_string,XSTR("<%s has joined>",717),Net_players[new_player_num].player->callsign);
1298 multi_display_chat_msg(notify_string,0,0);
1303 ml_printf(NOX("Received notification of new player %s"), Net_players[new_player_num].player->callsign);
1305 // let the current ui screen know someone joined
1306 switch(gameseq_get_state()){
1307 case GS_STATE_MULTI_HOST_SETUP :
1308 multi_create_handle_join(&Net_players[new_player_num]);
1310 case GS_STATE_MULTI_CLIENT_SETUP :
1311 multi_jw_handle_join(&Net_players[new_player_num]);
1316 #define PLAYER_DATA_SLOP 100
1318 void send_accept_player_data( net_player *npp, int is_ingame )
1322 ubyte data[MAX_PACKET_SIZE], stop;
1324 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1326 // add in the netplayer data for all players
1328 for (i=0; i<MAX_PLAYERS; i++) {
1329 // skip non connected players
1330 if ( !MULTI_CONNECTED(Net_players[i]) ){
1334 // skip this new player's entry
1335 if ( npp->player_id == Net_players[i].player_id ){
1339 // add the stop byte
1342 // add the player's number
1345 // add the player's address
1346 ADD_DATA(Net_players[i].p_info.addr);
1349 ADD_DATA(Net_players[i].player_id);
1352 ADD_STRING(Net_players[i].player->callsign);
1354 // add his image filename
1355 ADD_STRING(Net_players[i].player->image_filename);
1357 // add his squad filename
1358 ADD_STRING(Net_players[i].player->squad_filename);
1360 // add his PXO squad name
1361 ADD_STRING(Net_players[i].p_info.pxo_squad_name);
1364 ADD_DATA(Net_players[i].flags);
1366 // add his object's net sig
1368 ADD_DATA( Objects[Net_players[i].player->objnum].net_signature );
1371 if ( (packet_size + PLAYER_DATA_SLOP) > MAX_PACKET_SIZE ) {
1372 stop = APD_END_PACKET;
1374 multi_io_send_reliable( npp, data, packet_size );
1375 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1381 // add the stop byte
1382 stop = APD_END_DATA;
1384 multi_io_send_reliable(npp, data, packet_size);
1387 // send an accept packet to a client in response to a request to join the game
1388 void send_accept_packet(int new_player_num, int code, int ingame_join_team)
1391 ubyte data[MAX_PACKET_SIZE],val;
1392 char notify_string[256];
1395 Assert(new_player_num >= 0);
1397 // setup his "reliable" socket
1398 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1400 // build the packet header
1402 BUILD_HEADER(ACCEPT);
1404 // add the accept code
1407 // add code specific accept data
1408 if (code & ACCEPT_INGAME) {
1409 // the game filename
1410 ADD_STRING(Game_current_mission_filename);
1412 // if he is joining on a specific team, mark it here
1413 if(ingame_join_team != -1){
1416 val = (ubyte)ingame_join_team;
1424 if (code & ACCEPT_OBSERVER) {
1425 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1428 if (code & ACCEPT_HOST) {
1429 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1432 if (code & ACCEPT_CLIENT) {
1433 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1436 // add the current skill level setting on the host
1437 ADD_DATA(Game_skill_level);
1439 // add this guys player num
1440 ADD_DATA(new_player_num);
1442 // add his player id
1443 ADD_DATA(Net_players[new_player_num].player_id);
1445 // add netgame type flags
1446 ADD_DATA(Netgame.type_flags);
1449 // char buffer[100];
1450 // nprintf(("Network", "About to send accept packet to %s on port %d\n", get_text_address(buffer, addr->addr), addr->port ));
1453 // actually send the packet
1454 psnet_send(&Net_players[new_player_num].p_info.addr, data, packet_size);
1456 // if he's not an observer, inform all the other players in the game about him
1457 // inform the other players in the game about this new player
1458 for (i=0; i<MAX_PLAYERS; i++) {
1459 // skip unconnected players as well as this new guy himself
1460 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])) {
1464 // send the new packet
1465 send_new_player_packet(new_player_num,&Net_players[i]);
1468 // add a chat message
1469 if(Net_players[new_player_num].player->callsign != NULL){
1470 sprintf(notify_string,XSTR("<%s has joined>",717), Net_players[new_player_num].player->callsign);
1471 multi_display_chat_msg(notify_string, 0, 0);
1474 // handle any team vs. team details
1475 if (!(code & ACCEPT_OBSERVER)) {
1476 multi_team_handle_join(&Net_players[new_player_num]);
1480 if(Net_players[new_player_num].tracker_player_id >= 0){
1481 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);
1483 ml_printf(NOX("Server accepted %s as new client"), Net_players[new_player_num].player->callsign);
1488 // process the player data from the server
1489 void process_accept_player_data( ubyte *data, header *hinfo )
1491 int offset, player_num, player_slot_num, new_flags;
1492 char name[CALLSIGN_LEN + 1] = "";
1493 char image_name[MAX_FILENAME_LEN + 1] = "";
1494 char squad_name[MAX_FILENAME_LEN + 1] = "";
1495 char pxo_squad_name[LOGIN_LEN+1] = "";
1499 ushort ig_signature;
1501 offset = HEADER_LENGTH;
1504 while ( stop == APD_NEXT ) {
1505 player_slot_num = multi_find_open_player_slot();
1506 Assert(player_slot_num != -1);
1508 // get the player's number
1509 GET_DATA(player_num);
1511 // add the player's address
1514 // get the player's id#
1515 GET_DATA(player_id);
1520 // add his image filename
1521 GET_STRING(image_name);
1523 // get his squad logo filename
1524 GET_STRING(squad_name);
1526 // get his PXO squad name
1527 GET_STRING(pxo_squad_name);
1530 GET_DATA(new_flags);
1532 if (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) {
1533 if (!multi_obs_create_player(player_num, name, &addr, &Players[player_slot_num])) {
1538 // the error handling here is less than stellar. We should probably put up a popup and go
1539 // back to the main menu. But then again, this should never ever happen!
1540 if ( !multi_create_player(player_num, &Players[player_slot_num],name, &addr, -1, player_id) ) {
1545 // copy his image filename
1546 strcpy(Net_players[player_num].player->image_filename, image_name);
1548 // copy his pilot squad filename
1549 Net_players[player_num].player->insignia_texture = -1;
1550 player_set_squad_bitmap(Net_players[player_num].player, squad_name);
1552 // copy his pxo squad name
1553 strcpy(Net_players[player_num].p_info.pxo_squad_name, pxo_squad_name);
1555 // set his player id#
1556 Net_players[player_num].player_id = player_id;
1558 // mark him as being connected
1559 Net_players[player_num].flags |= NETINFO_FLAG_CONNECTED;
1560 Net_players[player_num].flags |= new_flags;
1562 // set the server pointer
1563 if ( Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER ) {
1564 Netgame.server = &Net_players[player_num];
1565 Netgame.server->last_heard_time = timer_get_fixed_seconds();
1567 // also - always set the server address to be where this data came from, NOT from
1568 // the data in the packet
1569 fill_net_addr(&Net_players[player_num].p_info.addr, hinfo->addr, hinfo->net_id, hinfo->port);
1572 // set the host pointer
1573 if ( Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST ) {
1574 Netgame.host = &Net_players[player_num];
1577 // read in the player's object net signature and store as his objnum for now
1578 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME ) {
1579 GET_DATA( ig_signature );
1580 Net_players[player_num].player->objnum = ig_signature;
1583 // get the stop byte
1588 if ( stop == APD_END_DATA ) {
1589 // if joining a game automatically, set the connect address to NULl so we don't try and
1590 // do this next time we enter a game
1591 if (Cmdline_connect_addr != NULL) {
1592 Cmdline_connect_addr = NULL;
1595 // send my stats to the server if I'm not in observer mode
1596 if (!(Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER)) {
1597 send_player_stats_block_packet(Net_player, STATS_ALLTIME);
1600 // if i'm being accepted as a host, then move into the host setup state
1601 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_HOST) {
1602 // set my permission bits
1603 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
1604 Net_player->state = NETPLAYER_STATE_STD_HOST_SETUP;
1606 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
1609 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER) {
1610 Net_player->flags |= NETINFO_FLAG_OBSERVER;
1612 // since observers can join 1 of 2 ways, only do this if we're not doing an ingame observer join
1613 if ( !(Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) ) {
1614 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1618 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_CLIENT) {
1619 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1622 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) {
1623 // flag myself as being an ingame joiner
1624 Net_player->flags |= NETINFO_FLAG_INGAME_JOIN;
1626 // move myself into the ingame join mission sync state
1627 Multi_sync_mode = MULTI_SYNC_INGAME;
1628 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
1631 // update my options on the server
1632 multi_options_update_local();
1634 // if we're in PXO mode, mark it down in our player struct
1635 if(MULTI_IS_TRACKER_GAME){
1636 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1637 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1642 // process an accept packet from the server
1643 extern int Select_default_ship;
1645 void process_accept_packet(ubyte* data, header* hinfo)
1647 int code, my_player_num, offset;
1651 // get the accept code
1652 offset = HEADER_LENGTH;
1656 // read in the accept code specific data
1658 if (code & ACCEPT_INGAME) {
1659 // the game filename
1660 GET_STRING(Game_current_mission_filename);
1661 Select_default_ship = 0;
1663 // determine if I'm being placed on a team
1670 if (code & ACCEPT_OBSERVER) {
1671 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1674 if (code & ACCEPT_HOST) {
1675 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1678 if (code & ACCEPT_CLIENT) {
1679 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1682 // fill in the netgame server address
1683 fill_net_addr( &Netgame.server_addr, hinfo->addr, hinfo->net_id, hinfo->port );
1685 // get the skill level setting
1686 GET_DATA(Game_skill_level);
1688 // get my netplayer number
1689 GET_DATA(my_player_num);
1692 GET_DATA(player_id);
1694 // get netgame type flags
1695 GET_DATA(Netgame.type_flags);
1697 // setup the Net_players structure for myself first
1698 Net_player = &Net_players[my_player_num];
1699 Net_player->flags = 0;
1700 Net_player->tracker_player_id = Multi_tracker_id;
1701 Net_player->player_id = player_id;
1702 Net_player->s_info.xfer_handle = -1;
1703 // stuff_netplayer_info( Net_player, &Psnet_my_addr, Ships[Objects[Player->objnum].instance].ship_info_index, Player );
1704 stuff_netplayer_info( Net_player, &Psnet_my_addr, 0, Player );
1705 multi_options_local_load(&Net_player->p_info.options, Net_player);
1707 Net_player->p_info.team = team;
1710 // determine if I have a CD
1712 Net_player->flags |= NETINFO_FLAG_HAS_CD;
1715 // set accept code in netplayer for this guy
1716 if ( code & ACCEPT_INGAME ){
1717 Net_player->flags |= NETINFO_FLAG_ACCEPT_INGAME;
1719 if ( code & ACCEPT_OBSERVER ){
1720 Net_player->flags |= NETINFO_FLAG_ACCEPT_OBSERVER;
1722 if ( code & ACCEPT_HOST ){
1723 Net_player->flags |= NETINFO_FLAG_ACCEPT_HOST;
1725 if ( code & ACCEPT_CLIENT ){
1726 Net_player->flags |= NETINFO_FLAG_ACCEPT_CLIENT;
1729 // if I have hacked data
1730 if(game_hacked_data()){
1731 Net_player->flags |= NETINFO_FLAG_HAXOR;
1734 // if we're supposed to flush our local data cache, do so now
1735 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
1736 multi_flush_multidata_cache();
1739 Net_player->sv_bytes_sent = 0;
1740 Net_player->sv_last_pl = -1;
1741 Net_player->cl_bytes_recvd = 0;
1742 Net_player->cl_last_pl = -1;
1744 // intiialize endgame stuff
1745 multi_endgame_init();
1749 // make a call to psnet to initialize and try to connect with the server.
1750 psnet_rel_connect_to_server( &Net_player->reliable_socket, &Netgame.server_addr );
1751 if ( Net_player->reliable_socket == INVALID_SOCKET ) {
1752 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONNECT_FAIL);
1756 // send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet)
1757 void send_leave_game_packet(short player_id, int kicked_reason, net_player *target)
1759 ubyte data[MAX_PACKET_SIZE];
1761 int packet_size = 0;
1763 BUILD_HEADER(LEAVE_GAME);
1765 // add a flag indicating whether he was kicked or not
1766 val = (char)kicked_reason;
1769 if (player_id < 0) {
1770 ADD_DATA(Net_player->player_id);
1772 // inform the host that we are leaving the game
1773 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1774 multi_io_send_to_all_reliable(data, packet_size);
1776 multi_io_send_reliable(Net_player, data, packet_size);
1779 // this is the case where to server is tossing a player (or indicating a respawned player has quit or become an observer)
1780 // so he has to tell everyone that this guy left
1782 nprintf(("Network","Sending a leave game packet to all players (server)\n"));
1784 // a couple of important checks
1785 Assert(player_id != Net_player->player_id);
1786 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1788 // add the id of the guy to be kicked
1789 ADD_DATA(player_id);
1791 // broadcast to everyone
1792 if (target == NULL) {
1793 multi_io_send_to_all_reliable(data, packet_size);
1795 multi_io_send_reliable(target, data, packet_size);
1800 // process a notification the a player has left the game
1801 void process_leave_game_packet(ubyte* data, header* hinfo)
1809 offset = HEADER_LENGTH;
1811 // get whether he was kicked
1812 GET_DATA(kicked_reason);
1814 // get the address of the guy who is to leave
1815 GET_DATA(deader_id);
1818 // determine who is dropping and printf out a notification
1819 player_num = find_player_id(deader_id);
1820 if (player_num == -1) {
1821 nprintf(("Network", "Received leave game packet for unknown player, ignoring\n"));
1825 nprintf(("Network", "Received a leave game notice for %s\n", Net_players[player_num].player->callsign));
1828 // a hook to display that a player was kicked
1829 if (kicked_reason >= 0){
1830 // if it was me that was kicked, leave the game
1831 if((Net_player != NULL) && (Net_player->player_id == deader_id)){
1834 switch(kicked_reason){
1835 case KICK_REASON_BAD_XFER:
1836 notify_code = MULTI_END_NOTIFY_KICKED_BAD_XFER;
1838 case KICK_REASON_CANT_XFER:
1839 notify_code = MULTI_END_NOTIFY_KICKED_CANT_XFER;
1841 case KICK_REASON_INGAME_ENDED:
1842 notify_code = MULTI_END_NOTIFY_KICKED_INGAME_ENDED;
1845 notify_code = MULTI_END_NOTIFY_KICKED;
1849 multi_quit_game(PROMPT_NONE, notify_code);
1852 // otherwise indicate someone was kicked
1854 nprintf(("Network","%s was kicked\n",Net_players[player_num].player->callsign));
1856 // display the result
1857 memset(str, 0, 512);
1858 multi_kick_get_text(&Net_players[player_num], kicked_reason, str);
1859 multi_display_chat_msg(str, player_num, 0);
1863 // first of all, if we're the master, we should be rebroadcasting this packet
1864 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1867 sprintf(msg, XSTR("%s has left the game",719), Net_players[player_num].player->callsign );
1869 if (!(Game_mode & GM_STANDALONE_SERVER)){
1870 HUD_sourced_printf(HUD_SOURCE_HIDDEN, msg);
1873 send_hud_msg_to_all(msg);
1874 multi_io_send_to_all_reliable(data, offset);
1877 // leave the game if the host and/or master has dropped
1879 if (((Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER) || (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)) ) {
1880 nprintf(("Network","Host and/or server has left the game - aborting...\n"));
1883 ml_string(NOX("Host and/or server has left the game"));
1885 // if the host leaves in the debriefing state, we should still wait until the player selects accept before we quit
1886 if (gameseq_get_state() != GS_STATE_DEBRIEF) {
1887 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
1890 delete_player(player_num);
1893 delete_player(player_num);
1895 // OSAPI GUI stuff (if standalone)
1896 if (Game_mode & GM_STANDALONE_SERVER) {
1897 // returns true if we should reset the standalone
1898 if (std_remove_player(&Net_players[player_num])) {
1899 nprintf(("Network", "Should reset!!\n"));
1903 // update these gui vals
1904 std_connect_set_host_connect_status();
1905 std_connect_set_connect_count();
1909 // send information about this currently active game to the specified address
1910 void send_game_active_packet(net_addr* addr)
1914 ubyte data[MAX_PACKET_SIZE],val;
1916 // build the header and add the data
1917 BUILD_HEADER(GAME_ACTIVE);
1919 // add the server version and compatible version #
1920 val = MULTI_FS_SERVER_VERSION;
1922 val = MULTI_FS_SERVER_COMPATIBLE_VERSION;
1925 ADD_STRING(Netgame.name);
1926 ADD_STRING(Netgame.mission_name);
1927 ADD_STRING(Netgame.title);
1928 val = (ubyte)multi_num_players();
1931 // add the proper flags
1933 if((Netgame.mode == NG_MODE_PASSWORD) || ((Game_mode & GM_STANDALONE_SERVER) && (multi_num_players() == 0) && (std_is_host_passwd()))){
1934 flags |= AG_FLAG_PASSWD;
1937 // proper netgame type flags
1938 if(Netgame.type_flags & NG_TYPE_TEAM){
1939 flags |= AG_FLAG_TEAMS;
1940 } else if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
1941 flags |= AG_FLAG_DOGFIGHT;
1943 flags |= AG_FLAG_COOP;
1946 // proper netgame state flags
1947 switch(Netgame.game_state){
1948 case NETGAME_STATE_FORMING:
1949 flags |= AG_FLAG_FORMING;
1952 case NETGAME_STATE_BRIEFING:
1953 case NETGAME_STATE_MISSION_SYNC:
1954 case NETGAME_STATE_HOST_SETUP:
1955 flags |= AG_FLAG_BRIEFING;
1958 case NETGAME_STATE_IN_MISSION:
1959 flags |= AG_FLAG_IN_MISSION;
1962 case NETGAME_STATE_PAUSED:
1963 flags |= AG_FLAG_PAUSE;
1966 case NETGAME_STATE_ENDGAME:
1967 case NETGAME_STATE_DEBRIEF:
1968 flags |= AG_FLAG_DEBRIEF;
1972 // if this is a standalone
1973 if(Game_mode & GM_STANDALONE_SERVER){
1974 flags |= AG_FLAG_STANDALONE;
1977 // if we're in campaign mode
1978 if(Netgame.campaign_mode == MP_CAMPAIGN){
1979 flags |= AG_FLAG_CAMPAIGN;
1982 // add the data about the connection speed of the host machine
1983 Assert( (Multi_connection_speed >= 0) && (Multi_connection_speed <= 4) );
1984 flags |= (Multi_connection_speed << AG_FLAG_CONNECTION_BIT);
1989 psnet_send(addr, data, packet_size);
1992 // process information about an active game
1993 void process_game_active_packet(ubyte* data, header* hinfo)
1998 int modes_compatible;
2000 fill_net_addr(&ag.server_addr, hinfo->addr, hinfo->net_id, hinfo->port);
2002 // read this game into a temporary structure
2003 offset = HEADER_LENGTH;
2005 // get the server version and compatible version
2006 GET_DATA(ag.version);
2007 GET_DATA(ag.comp_version);
2009 GET_STRING(ag.name);
2010 GET_STRING(ag.mission_name);
2011 GET_STRING(ag.title);
2013 ag.num_players = val;
2018 modes_compatible = 1;
2020 if((ag.flags & AG_FLAG_TRACKER) && !Multi_options_g.pxo){
2021 modes_compatible = 0;
2023 if(!(ag.flags & AG_FLAG_TRACKER) && Multi_options_g.pxo){
2024 modes_compatible = 0;
2028 // if this is a compatible version, and our modes are compatible, register it
2029 if( (ag.version == MULTI_FS_SERVER_VERSION) && modes_compatible ){
2030 multi_update_active_games(&ag);
2034 // send_game_update_packet sends an updated Netgame structure to all players currently connected. The update
2035 // is used to change the current mission, current state, etc.
2036 void send_netgame_update_packet(net_player *pl)
2040 ubyte data[MAX_PACKET_SIZE];
2043 BUILD_HEADER(GAME_UPDATE);
2045 // with new mission description field, this becomes way to large
2046 // so we must add every element piece by piece except the
2047 ADD_STRING(Netgame.name);
2048 ADD_STRING(Netgame.mission_name);
2049 ADD_STRING(Netgame.title);
2050 ADD_STRING(Netgame.campaign_name);
2051 ADD_DATA(Netgame.campaign_mode);
2052 ADD_DATA(Netgame.max_players);
2053 ADD_DATA(Netgame.security);
2054 ADD_DATA(Netgame.respawn);
2055 ADD_DATA(Netgame.flags);
2056 ADD_DATA(Netgame.type_flags);
2057 ADD_DATA(Netgame.version_info);
2058 ADD_DATA(Netgame.debug_flags);
2060 // only the server should ever send the netgame state (standalone situation)
2061 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2062 ADD_DATA(Netgame.game_state);
2065 // if we're the host on a standalone, send to the standalone and let him rebroadcast
2066 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2068 multi_io_send_to_all_reliable(data, packet_size);
2070 for(idx=0; idx<MAX_PLAYERS; idx++){
2071 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
2072 send_netgame_descript_packet(&Net_players[idx].p_info.addr, 1);
2076 multi_io_send_reliable(pl, data, packet_size);
2077 send_netgame_descript_packet( &pl->p_info.addr , 1 );
2080 Assert( pl == NULL ); // I don't think that a host in a standalone game would get here.
2081 multi_io_send_reliable(Net_player, data, packet_size);
2084 // host should always send a netgame options update as well
2085 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2086 multi_options_update_netgame();
2090 // process information about the netgame sent from the server/host
2091 void process_netgame_update_packet( ubyte *data, header *hinfo )
2093 int offset,old_flags;
2096 Assert(!(Game_mode & GM_STANDALONE_SERVER));
2097 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
2099 // read in the netgame information
2100 offset = HEADER_LENGTH;
2101 GET_STRING(Netgame.name);
2102 GET_STRING(Netgame.mission_name);
2103 GET_STRING(Netgame.title);
2104 GET_STRING(Netgame.campaign_name);
2105 GET_DATA(Netgame.campaign_mode);
2106 GET_DATA(Netgame.max_players); // ignore on the standalone, who keeps track of this himself
2107 GET_DATA(Netgame.security);
2108 GET_DATA(Netgame.respawn);
2110 // be sure not to blast the quitting flag because of the "one frame extra" problem
2111 old_flags = Netgame.flags;
2112 GET_DATA(Netgame.flags);
2113 GET_DATA(Netgame.type_flags);
2114 GET_DATA(Netgame.version_info);
2115 GET_DATA(Netgame.debug_flags);
2122 // now compare the passed in game state to our current known state. If it has changed, then maybe
2123 // do something interesting.
2124 // move from the forming or debriefing state to the mission sync state
2125 if ( ng_state == NETGAME_STATE_MISSION_SYNC ){
2126 // if coming from the forming state
2127 if( (Netgame.game_state == NETGAME_STATE_FORMING) ||
2128 ((Netgame.game_state != NETGAME_STATE_FORMING) && ((gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP) || (gameseq_get_state() == GS_STATE_MULTI_CLIENT_SETUP))) ){
2129 // do any special processing for forced state transitions
2130 multi_handle_state_special();
2132 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2133 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2134 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2136 // if coming from the debriefing state
2137 else if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2138 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2140 // do any special processing for forced state transitions
2141 multi_handle_state_special();
2143 multi_flush_mission_stuff();
2145 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2146 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2147 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2150 // move from mission sync to team select
2151 else if ( ng_state == NETGAME_STATE_BRIEFING ){
2152 if( (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ||
2153 ((Netgame.game_state != NETGAME_STATE_MISSION_SYNC) && (gameseq_get_state() == GS_STATE_MULTI_MISSION_SYNC) && (Multi_sync_mode != MULTI_SYNC_POST_BRIEFING)) ){
2155 // do any special processing for forced state transitions
2156 multi_handle_state_special();
2158 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2159 gameseq_post_event(GS_EVENT_START_BRIEFING);
2162 // move from the debriefing to the create game screen
2163 else if ( ng_state == NETGAME_STATE_FORMING ){
2164 if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2165 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2166 // do any special processing for forced state transitions
2167 multi_handle_state_special();
2169 multi_flush_mission_stuff();
2171 // move to the proper screen
2172 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2173 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2175 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
2180 Netgame.game_state = ng_state;
2183 // send a request or a reply for mission description, if code == 0, request, if code == 1, reply
2184 void send_netgame_descript_packet(net_addr *addr, int code)
2186 ubyte data[MAX_PACKET_SIZE],val;
2188 int packet_size = 0;
2191 BUILD_HEADER(UPDATE_DESCRIPT);
2197 // add as much of the description as we dare
2198 len = strlen(The_mission.mission_desc);
2199 if(len > MAX_PACKET_SIZE - 10){
2200 len = MAX_PACKET_SIZE - 10;
2202 memcpy(data+packet_size,The_mission.mission_desc,len);
2205 ADD_STRING(The_mission.mission_desc);
2209 Assert(addr != NULL);
2211 psnet_send(addr, data, packet_size);
2215 // process an incoming netgame description packet
2216 void process_netgame_descript_packet( ubyte *data, header *hinfo )
2220 char mission_desc[MISSION_DESC_LENGTH+2];
2223 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2225 // read this game into a temporary structure
2226 offset = HEADER_LENGTH;
2229 // if this is a request for mission description
2231 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2236 // send an update to this guy
2237 send_netgame_descript_packet(&addr, 1);
2239 memset(mission_desc,0,MISSION_DESC_LENGTH+2);
2240 GET_STRING(mission_desc);
2242 // only display if we're in the proper state
2243 state = gameseq_get_state();
2245 case GS_STATE_MULTI_JOIN_GAME:
2246 case GS_STATE_MULTI_CLIENT_SETUP:
2247 case GS_STATE_MULTI_HOST_SETUP:
2248 multi_common_set_text(mission_desc);
2256 // broadcast a query for active games. IPX will use net broadcast and TCP will either request from the MT or from the specified list
2257 void broadcast_game_query()
2261 server_item *s_moveup;
2262 ubyte data[MAX_PACKET_SIZE];
2264 BUILD_HEADER(GAME_QUERY);
2266 // go through the server list and query each of those as well
2267 s_moveup = Game_server_head;
2268 if(s_moveup != NULL){
2270 send_server_query(&s_moveup->server_addr);
2271 s_moveup = s_moveup->next;
2272 } while(s_moveup != Game_server_head);
2275 fill_net_addr(&addr, Psnet_my_addr.addr, Psnet_my_addr.net_id, DEFAULT_GAME_PORT);
2277 // send out a broadcast if our options allow us
2278 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2279 psnet_broadcast( &addr, data, packet_size);
2283 // send an individual query to an address to see if there is an active game
2284 void send_server_query(net_addr *addr)
2287 ubyte data[MAX_PACKET_SIZE];
2289 // build the header and send the data
2290 BUILD_HEADER(GAME_QUERY);
2291 psnet_send(addr, data, packet_size);
2294 // process a query from a client looking for active freespace games
2295 void process_game_query(ubyte* data, header* hinfo)
2300 offset = HEADER_LENGTH;
2304 // check to be sure that we don't capture our own broadcast message
2305 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2306 if ( psnet_same( &addr, &Psnet_my_addr) ){
2310 // if I am not a server of a game, don't send a reply!!!
2311 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
2315 // if the game options are being selected, then ignore the request
2316 // also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
2317 if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
2321 // send information about this active game
2322 send_game_active_packet(&addr);
2325 // sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
2326 void send_netplayer_update_packet( net_player *pl )
2328 int packet_size,idx;
2329 ubyte data[MAX_PACKET_SIZE],val;
2331 BUILD_HEADER(NETPLAYER_UPDATE);
2333 // if I'm the server of the game, I should send an update for _all_players in the game
2334 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2335 for(idx=0;idx<MAX_PLAYERS;idx++){
2336 // only send info for connected players
2337 if(MULTI_CONNECTED(Net_players[idx])){
2342 // add the net player's information
2343 ADD_DATA(Net_players[idx].player_id);
2344 ADD_DATA(Net_players[idx].state);
2345 ADD_DATA(Net_players[idx].p_info.ship_class);
2346 ADD_DATA(Net_players[idx].tracker_player_id);
2348 if(Net_players[idx].flags & NETINFO_FLAG_HAS_CD){
2356 // add the final stop byte
2360 // broadcast the packet
2361 if(!(Game_mode & GM_IN_MISSION)){
2363 multi_io_send_to_all_reliable(data, packet_size);
2365 multi_io_send_reliable(pl, data, packet_size);
2369 multi_io_send_to_all(data, packet_size);
2371 multi_io_send(pl, data, packet_size);
2379 // add my current state in the netgame to this packet
2380 ADD_DATA(Net_player->player_id);
2381 ADD_DATA(Net_player->state);
2382 ADD_DATA(Net_player->p_info.ship_class);
2383 ADD_DATA(Multi_tracker_id);
2385 // add if I have a CD or not
2393 // add a final stop byte
2397 // send the packet to the server
2398 Assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
2399 if(!(Game_mode & GM_IN_MISSION)){
2400 multi_io_send_reliable(Net_player, data, packet_size);
2402 multi_io_send(Net_player, data, packet_size);
2407 // process an incoming netplayer state update. if we're the server, we should rebroadcast
2408 void process_netplayer_update_packet( ubyte *data, header *hinfo )
2410 int offset, player_num;
2416 offset = HEADER_LENGTH;
2418 // get the first stop byte
2421 while(stop != 0xff){
2422 // look the player up
2423 GET_DATA(player_id);
2424 player_num = find_player_id(player_id);
2425 // if we couldn't find him, read in the bogus data
2426 if((player_num == -1) || (Net_player == &Net_players[player_num])){
2427 GET_DATA(bogus.state);
2428 GET_DATA(bogus.p_info.ship_class);
2429 GET_DATA(bogus.tracker_player_id);
2433 // otherwise read in the data correctly
2435 GET_DATA(new_state);
2436 GET_DATA(Net_players[player_num].p_info.ship_class);
2437 GET_DATA(Net_players[player_num].tracker_player_id);
2440 Net_players[player_num].flags |= NETINFO_FLAG_HAS_CD;
2442 Net_players[player_num].flags &= ~(NETINFO_FLAG_HAS_CD);
2445 // if he's changing state to joined, send a team update
2446 if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
2447 multi_team_send_update();
2451 Net_players[player_num].state = new_state;
2454 // get the next stop byte
2460 // if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
2461 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2462 send_netplayer_update_packet(NULL);
2465 // if i'm the standalone and this is an update from the host, maybe change some netgame settings
2466 if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
2467 switch(Net_players[player_num].state){
2468 case NETPLAYER_STATE_STD_HOST_SETUP:
2469 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2472 case NETPLAYER_STATE_HOST_SETUP:
2473 // check for race conditions
2474 if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
2475 Netgame.game_state = NETGAME_STATE_FORMING;
2482 #define EXTRA_DEATH_VAPORIZED (1<<0)
2483 #define EXTRA_DEATH_WASHED (1<<1)
2484 // send a packet indicating a ship has been killed
2485 void send_ship_kill_packet( object *objp, object *other_objp, float percent_killed, int self_destruct )
2487 int packet_size, model;
2488 ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
2489 ushort debris_signature;
2493 // only sendable from the master
2494 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
2497 vaporized = ( (Ships[objp->instance].flags & SF_VAPORIZE) > 0 );
2499 extra_death_info = 0;
2501 extra_death_info |= EXTRA_DEATH_VAPORIZED;
2504 if ( Ships[objp->instance].wash_killed ) {
2505 extra_death_info |= EXTRA_DEATH_WASHED;
2508 // find out the next network signature that will be used for the debris pieces.
2509 model = Ships[objp->instance].modelnum;
2510 pm = model_get(model);
2511 debris_signature = 0;
2512 if ( pm && !vaporized ) {
2513 debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
2514 multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
2515 Ships[objp->instance].arrival_distance = debris_signature;
2518 BUILD_HEADER(SHIP_KILL);
2519 ADD_DATA(objp->net_signature);
2521 // ships which are initially killed get the rest of the data sent. self destructed ships and
2522 if ( other_objp == NULL ) {
2527 nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
2529 ADD_DATA( other_objp->net_signature );
2532 ADD_DATA( debris_signature );
2533 ADD_DATA( percent_killed );
2534 sd = (ubyte)self_destruct;
2536 ADD_DATA( extra_death_info );
2538 // if the ship who died is a player, then send some extra info, like who killed him, etc.
2540 if ( objp->flags & OF_PLAYER_SHIP ) {
2544 pnum = multi_find_player_by_object( objp );
2547 ADD_DATA( was_player );
2549 Assert(Net_players[pnum].player->killer_objtype < CHAR_MAX);
2550 temp = (char)Net_players[pnum].player->killer_objtype;
2553 Assert(Net_players[pnum].player->killer_species < CHAR_MAX);
2554 temp = (char)Net_players[pnum].player->killer_species;
2557 Assert(Net_players[pnum].player->killer_weapon_index < CHAR_MAX);
2558 temp = (char)Net_players[pnum].player->killer_weapon_index;
2561 ADD_STRING( Net_players[pnum].player->killer_parent_name );
2563 ADD_DATA( was_player );
2566 ADD_DATA( was_player );
2569 // send the packet reliably!!!
2570 multi_io_send_to_all_reliable(data, packet_size);
2573 // process a packet indicating that a ship has been killed
2574 void process_ship_kill_packet( ubyte *data, header *hinfo )
2577 ushort ship_sig, other_sig, debris_sig;
2578 object *sobjp, *oobjp;
2579 float percent_killed;
2580 ubyte was_player, extra_death_info, sd;
2581 char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = SPECIES_TERRAN, killer_weapon_index = -1;
2583 offset = HEADER_LENGTH;
2586 GET_DATA( other_sig );
2587 GET_DATA( debris_sig );
2588 GET_DATA( percent_killed );
2590 GET_DATA( extra_death_info );
2591 GET_DATA( was_player );
2594 // pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
2595 if ( was_player != 0 ) {
2596 GET_DATA( killer_objtype );
2597 GET_DATA( killer_species );
2598 GET_DATA( killer_weapon_index );
2599 GET_STRING( killer_name );
2604 sobjp = multi_get_network_object( ship_sig );
2606 // if I am unable to find the ship object which was killed, I have to bail and rely on getting
2607 // another message from the server that this happened!
2608 if ( sobjp == NULL ) {
2609 nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
2613 // set this ship's hull value to 0
2614 sobjp->hull_strength = 0.0f;
2616 // maybe set vaporized
2617 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2618 Ships[sobjp->instance].flags |= SF_VAPORIZE;
2621 // maybe set wash_killed
2622 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2623 Ships[sobjp->instance].wash_killed = 1;
2626 oobjp = multi_get_network_object( other_sig );
2628 if ( was_player != 0 ) {
2631 pnum = multi_find_player_by_object( sobjp );
2633 Net_players[pnum].player->killer_objtype = killer_objtype;
2634 Net_players[pnum].player->killer_species = killer_species;
2635 Net_players[pnum].player->killer_weapon_index = killer_weapon_index;
2636 strcpy( Net_players[pnum].player->killer_parent_name, killer_name );
2640 // check to see if I need to respawn myself
2641 multi_respawn_check(sobjp);
2643 // store the debris signature in the arrival distance which will never get used for player ships
2644 Ships[sobjp->instance].arrival_distance = debris_sig;
2646 // set this bit so that we don't accidentally start switching targets when we die
2647 if(sobjp == Player_obj){
2648 Game_mode |= GM_DEAD_DIED;
2651 nprintf(("Network", "Killing off %s\n", Ships[sobjp->instance].ship_name));
2653 // do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
2654 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2655 ship_hit_kill( sobjp, oobjp, percent_killed, sd );
2657 extern void ship_destroyed( int shipnum );
2658 ship_destroyed( sobjp->instance );
2659 sobjp->flags |= OF_SHOULD_BE_DEAD;
2660 obj_delete( OBJ_INDEX(sobjp) );
2664 // send a packet indicating a ship should be created
2665 void send_ship_create_packet( object *objp, int is_support )
2668 ubyte data[MAX_PACKET_SIZE];
2670 // We will pass the ship to create by name.
2671 BUILD_HEADER(SHIP_CREATE);
2672 ADD_DATA(objp->net_signature);
2673 ADD_DATA( is_support );
2675 ADD_DATA( objp->pos );
2678 // broadcast the packet
2679 multi_io_send_to_all_reliable(data, packet_size);
2682 // process a packet indicating a ship should be created
2683 void process_ship_create_packet( ubyte *data, header *hinfo )
2685 int offset, objnum, is_support;
2688 vector pos = ZERO_VECTOR;
2690 Assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
2691 offset = HEADER_LENGTH;
2692 GET_DATA(signature);
2693 GET_DATA( is_support );
2700 // find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
2701 if ( !is_support ) {
2702 objp = mission_parse_get_arrival_ship( signature );
2703 if ( objp != NULL ) {
2704 objnum = parse_create_object(objp);
2706 nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
2709 Assert( Arriving_support_ship );
2710 if(Arriving_support_ship == NULL){
2713 Arriving_support_ship->pos = pos;
2714 Arriving_support_ship->net_signature = signature;
2715 objnum = parse_create_object( Arriving_support_ship );
2716 Assert( objnum != -1 );
2718 mission_parse_support_arrived( objnum );
2723 // send a packet indicating a wing of ships should be created
2724 void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
2726 int packet_size, index, ship_instance;
2727 ubyte data[MAX_PACKET_SIZE];
2731 // for creating wing -- we just send the index into the wing array of this wing.
2732 // all players load the same mission, and so their array's should all match. We also
2733 // need to send the signature of the first ship that was created. We can find this by
2734 // looking num_to_create places back in the ship_index field in the wing structure.
2736 index = WING_INDEX(wingp);
2737 ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
2738 signature = Objects[Ships[ship_instance].objnum].net_signature;
2740 BUILD_HEADER( WING_CREATE );
2742 ADD_DATA(num_to_create);
2743 ADD_DATA(signature);
2744 ADD_DATA(pre_create_count);
2745 val = wingp->current_wave - 1;
2748 multi_io_send_to_all_reliable(data, packet_size);
2751 // process a packet saying that a wing should be created
2752 void process_wing_create_packet( ubyte *data, header *hinfo )
2754 int offset, index, num_to_create;
2756 int total_arrived_count, current_wave;
2758 offset = HEADER_LENGTH;
2760 GET_DATA(num_to_create);
2761 GET_DATA(signature);
2762 GET_DATA(total_arrived_count);
2763 GET_DATA(current_wave);
2767 // do a sanity check on the wing to be sure that we are actually working on a valid wing
2768 if ( (index < 0) || (index >= num_wings) || (Wings[index].num_waves == -1) ) {
2769 nprintf(("Network", "invalid index %d for wing create packet\n"));
2772 if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
2773 nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
2778 Wings[index].current_count = 0;
2779 Wings[index].total_arrived_count = total_arrived_count;
2780 Wings[index].current_wave = current_wave;
2782 // set the network signature that was passed. The client should create ships in the same order
2783 // as the server -- so all ships should get the same sigs as assigned by the server. We also
2784 // need to set some timestamps and cues correctly to be sure that these things get created on
2785 // the clients correctly
2786 multi_set_network_signature( signature, MULTI_SIG_SHIP );
2787 parse_wing_create_ships( &Wings[index], num_to_create, 1 );
2790 // packet indicating a ship is departing
2791 void send_ship_depart_packet( object *objp )
2793 ubyte data[MAX_PACKET_SIZE];
2797 signature = objp->net_signature;
2799 BUILD_HEADER(SHIP_DEPART);
2800 ADD_DATA( signature );
2802 multi_io_send_to_all_reliable(data, packet_size);
2805 // process a packet indicating a ship is departing
2806 void process_ship_depart_packet( ubyte *data, header *hinfo )
2812 offset = HEADER_LENGTH;
2813 GET_DATA( signature );
2816 // find the object which is departing
2817 objp = multi_get_network_object( signature );
2818 if ( objp == NULL ) {
2819 nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
2823 // start warping him out
2824 shipfx_warpout_start( objp );
2827 // packet to tell clients cargo of a ship was revealed to all
2828 void send_cargo_revealed_packet( ship *shipp )
2830 ubyte data[MAX_PACKET_SIZE];
2833 // build the header and add the data
2834 BUILD_HEADER(CARGO_REVEALED);
2835 ADD_DATA( Objects[shipp->objnum].net_signature );
2837 // server sends to all players
2838 if(MULTIPLAYER_MASTER){
2839 multi_io_send_to_all_reliable(data, packet_size);
2841 // clients just send to the server
2843 multi_io_send_reliable(Net_player, data, packet_size);
2847 // process a cargo revealed packet
2848 void process_cargo_revealed_packet( ubyte *data, header *hinfo )
2854 offset = HEADER_LENGTH;
2855 GET_DATA(signature);
2858 // get a ship pointer and call the ship function to reveal the cargo
2859 objp = multi_get_network_object( signature );
2860 if ( objp == NULL ) {
2861 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
2865 // Assert( objp->type == OBJ_SHIP );
2866 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2870 // this will take care of re-routing to all other clients
2871 ship_do_cargo_revealed( &Ships[objp->instance], 1);
2873 // server should rebroadcast
2874 if(MULTIPLAYER_MASTER){
2875 send_cargo_revealed_packet(&Ships[objp->instance]);
2879 // defines used for secondary fire packet
2880 #define SFPF_ALLOW_SWARM (1<<7)
2881 #define SFPF_DUAL_FIRE (1<<6)
2882 #define SFPF_TARGET_LOCKED (1<<5)
2884 // send a packet indicating a secondary weapon was fired
2885 void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm )
2887 int packet_size, net_player_num;
2888 ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
2890 ushort target_signature;
2894 // Assert ( starting_count < UCHAR_MAX );
2896 // get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
2897 // we might send the info to the other player different than the one who fired
2898 objp = &Objects[shipp->objnum];
2899 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
2900 if ( num_fired == 0 ) {
2905 aip = &Ai_info[shipp->ai_index];
2907 current_bank = (ubyte)shipp->weapons.current_secondary_bank;
2908 Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) );
2910 // build up the header portion
2911 BUILD_HEADER( SECONDARY_FIRED_AI );
2913 ADD_DATA( Objects[shipp->objnum].net_signature );
2914 ADD_DATA( starting_sig );
2916 // add a couple of bits for swarm missiles and dual fire secondary weaspons
2919 sinfo = current_bank;
2922 sinfo |= SFPF_ALLOW_SWARM;
2925 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ){
2926 sinfo |= SFPF_DUAL_FIRE;
2929 if ( aip->current_target_is_locked ){
2930 sinfo |= SFPF_TARGET_LOCKED;
2935 // add the ship's target and any targeted subsystem
2936 target_signature = 0;
2938 if ( aip->target_objnum != -1) {
2939 target_signature = Objects[aip->target_objnum].net_signature;
2940 if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
2943 s_index = ship_get_index_from_subsys( aip->targeted_subsys, aip->target_objnum );
2944 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
2945 t_subsys = (char)s_index;
2948 if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
2949 Assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags & WIF_BOMB);
2954 ADD_DATA( target_signature );
2955 ADD_DATA( t_subsys );
2957 // just send this packet to everyone, then bail if an AI ship fired.
2958 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
2959 multi_io_send_to_all(data, packet_size);
2963 net_player_num = multi_find_player_by_object( objp );
2965 // getting here means a player fired. Send the current packet to all players except the player
2966 // who fired. If nothing got fired, then don't send to the other players -- we will just send
2967 // a packet to the player who will find out that he didn't fire anything
2968 if ( num_fired > 0 ) {
2969 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
2972 // if I (the master) fired, then return
2973 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
2977 // now build up the packet to send to the player who actually fired.
2978 BUILD_HEADER( SECONDARY_FIRED_PLR );
2979 ADD_DATA(starting_sig);
2982 // add the targeting information so that the player's weapons will always home on the correct
2984 ADD_DATA( target_signature );
2985 ADD_DATA( t_subsys );
2987 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
2990 /// process a packet indicating a secondary weapon was fired
2991 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
2993 int offset, allow_swarm, target_objnum_save;
2994 ushort net_signature, starting_sig, target_signature;
2995 ubyte sinfo, current_bank;
2996 object* objp, *target_objp;
3000 ship_subsys *targeted_subsys_save;
3002 offset = HEADER_LENGTH; // size of the header
3004 // if from_player is false, it means that the secondary weapon info in this packet was
3005 // fired by an ai object (or another player). from_player == 1 means tha me (the person
3006 // receiving this packet) fired the secondary weapon
3007 if ( !from_player ) {
3008 GET_DATA( net_signature );
3009 GET_DATA( starting_sig );
3010 GET_DATA( sinfo ); // are we firing swarm missiles
3012 GET_DATA( target_signature );
3013 GET_DATA( t_subsys );
3017 // find the object (based on network signatures) for the object that fired
3018 objp = multi_get_network_object( net_signature );
3019 if ( objp == NULL ) {
3020 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3024 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3025 // of the next fire time of this bank to 0 so we can fire right away
3026 shipp = &Ships[objp->instance];
3029 GET_DATA( starting_sig );
3032 GET_DATA( target_signature );
3033 GET_DATA( t_subsys );
3037 // get the object and ship
3039 shipp = Player_ship;
3042 // check the allow swarm bit
3044 if ( sinfo & SFPF_ALLOW_SWARM ){
3048 // set the dual fire properties of the ship
3049 if ( sinfo & SFPF_DUAL_FIRE ){
3050 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3052 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3055 // determine whether current target is locked
3056 Assert( shipp->ai_index != -1 );
3057 aip = &Ai_info[shipp->ai_index];
3058 if ( sinfo & SFPF_TARGET_LOCKED ) {
3059 aip->current_target_is_locked = 1;
3061 aip->current_target_is_locked = 0;
3064 // find out the current bank
3065 current_bank = (ubyte)(sinfo & 0x3);
3066 Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) );
3067 shipp->weapons.current_secondary_bank = current_bank;
3069 // make it so we can fire this ship's secondary bank immediately!!!
3070 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3071 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3073 // set this ship's target and subsystem information. We will save and restore target and
3074 // targeted subsystem so that we do not accidentally change targets for this player or
3075 // any AI ships on his system.
3076 target_objnum_save = aip->target_objnum;
3077 targeted_subsys_save = aip->targeted_subsys;
3079 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3080 aip->target_objnum = -1;
3081 aip->targeted_subsys = NULL;
3083 target_objp = multi_get_network_object( target_signature );
3084 if ( target_objp != NULL ) {
3085 aip->target_objnum = OBJ_INDEX(target_objp);
3087 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3088 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3092 if ( starting_sig != 0 ){
3093 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3095 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3098 ship_fire_secondary( objp, allow_swarm );
3100 // restore targeted object and targeted subsystem
3101 aip->target_objnum = target_objnum_save;
3102 aip->targeted_subsys = targeted_subsys_save;
3105 // send a packet indicating a countermeasure was fired
3106 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3108 ubyte data[MAX_PACKET_SIZE];
3113 Assert ( cmeasure_count < UCHAR_MAX );
3114 BUILD_HEADER(COUNTERMEASURE_FIRED);
3115 ADD_DATA( objp->net_signature );
3116 ADD_DATA( rand_val );
3118 multi_io_send_to_all(data, packet_size);
3121 // process a packet indicating a countermeasure was fired
3122 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3124 int offset, rand_val;
3130 offset = HEADER_LENGTH;
3132 GET_DATA( signature );
3133 GET_DATA( rand_val );
3136 objp = multi_get_network_object( signature );
3137 if ( objp == NULL ) {
3138 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3141 if(objp->type != OBJ_SHIP){
3144 // Assert ( objp->type == OBJ_SHIP );
3146 // make it so ship can fire right away!
3147 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3148 if ( objp == Player_obj ){
3149 nprintf(("network", "firing countermeasure from my ship\n"));
3152 ship_launch_countermeasure( objp, rand_val );
3155 // send a packet indicating that a turret has been fired
3156 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3159 ushort pnet_signature;
3160 ubyte data[MAX_PACKET_SIZE], cindex;
3167 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3171 // local setup -- be sure we are actually passing a weapon!!!!
3172 objp = &Objects[weapon_objnum];
3173 Assert ( objp->type == OBJ_WEAPON );
3174 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3178 pnet_signature = Objects[ship_objnum].net_signature;
3180 Assert( subsys_index < UCHAR_MAX );
3181 cindex = (ubyte)subsys_index;
3183 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3188 // build the fire turret packet.
3189 BUILD_HEADER(FIRE_TURRET_WEAPON);
3190 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
3191 ADD_DATA( has_sig );
3192 ADD_DATA( pnet_signature );
3194 ADD_DATA( objp->net_signature );
3197 val = (short)ssp->submodel_info_1.angs.h;
3199 val = (short)ssp->submodel_info_2.angs.p;
3202 multi_io_send_to_all(data, packet_size);
3204 multi_rate_add(1, "tur", packet_size);
3207 // process a packet indicating a turret has been fired
3208 void process_turret_fired_packet( ubyte *data, header *hinfo )
3210 int offset, weapon_objnum, wid;
3211 ushort pnet_signature, wnet_signature;
3220 short pitch, heading;
3222 // get the data for the turret fired packet
3223 offset = HEADER_LENGTH;
3224 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3225 GET_DATA( has_sig );
3226 GET_DATA( pnet_signature );
3228 GET_DATA( wnet_signature );
3232 GET_DATA( turret_index );
3233 GET_DATA( heading );
3235 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3238 objp = multi_get_network_object( pnet_signature );
3239 if ( objp == NULL ) {
3240 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3244 // if this isn't a ship, do nothing
3245 if ( objp->type != OBJ_SHIP ){
3249 // make an orientation matrix from the o_fvec
3250 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3252 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3253 // hack, but should be suitable.
3254 shipp = &Ships[objp->instance];
3255 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3259 wid = ssp->system_info->turret_weapon_type;
3261 // bash the position and orientation of the turret
3262 ssp->submodel_info_1.angs.h = (float)heading;
3263 ssp->submodel_info_2.angs.p = (float)pitch;
3265 // get the world position of the weapon
3266 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3268 // create the weapon object
3269 if(wnet_signature != 0){
3270 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3272 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3273 if (weapon_objnum != -1) {
3274 if ( Weapon_info[wid].launch_snd != -1 ) {
3275 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3280 // send a mission log item packet
3281 void send_mission_log_packet( int num )
3284 ubyte data[MAX_PACKET_SIZE];
3289 Assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3291 // get the data from the log
3292 entry = &log_entries[num];
3293 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3294 sindex = (ushort)entry->index;
3296 BUILD_HEADER(MISSION_LOG_ENTRY);
3298 ADD_DATA(entry->flags);
3300 ADD_DATA(entry->timestamp);
3301 ADD_STRING(entry->pname);
3302 ADD_STRING(entry->sname);
3304 // broadcast the packet to all players
3305 multi_io_send_to_all_reliable(data, packet_size);
3308 // process a mission log item packet
3309 void process_mission_log_packet( ubyte *data, header *hinfo )
3314 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3317 Assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3319 offset = HEADER_LENGTH;
3323 GET_DATA(timestamp);
3329 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3332 // send a mission message packet
3333 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)
3336 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3338 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3339 Assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3340 Assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3342 up = (ubyte) priority;
3343 us = (ubyte) source;
3344 utime = (ubyte)timing;
3346 BUILD_HEADER(MISSION_MESSAGE);
3348 ADD_STRING(who_from);
3352 ADD_DATA(builtin_type);
3353 ADD_DATA(multi_team_filter);
3355 if (multi_target == -1){
3356 multi_io_send_to_all_reliable(data, packet_size);
3358 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3362 // process a mission message packet
3363 void process_mission_message_packet( ubyte *data, header *hinfo )
3365 int offset, id, builtin_type;
3366 ubyte priority, source, utiming;
3367 char who_from[NAME_LENGTH];
3368 int multi_team_filter;
3370 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3372 offset = HEADER_LENGTH;
3374 GET_STRING(who_from);
3378 GET_DATA(builtin_type);
3379 GET_DATA(multi_team_filter);
3383 // filter out builtin ones in TvT
3384 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3388 // maybe filter this out
3389 if(!message_filter_multi(id)){
3390 // send the message as if it came from an sexpression
3391 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3395 // just send them a pong back as fast as possible
3396 void process_ping_packet(ubyte *data, header *hinfo)
3401 offset = HEADER_LENGTH;
3404 // get the address to return the pong to
3405 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3411 // right now it just routes the pong through to the standalone gui, which is the only
3412 // system which uses ping and pong right now.
3413 void process_pong_packet(ubyte *data, header *hinfo)
3419 offset = HEADER_LENGTH;
3421 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3425 // if we're connected , see who sent us this pong
3426 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3427 lookup = find_player_id(hinfo->id);
3432 p = &Net_players[lookup];
3434 // evaluate the ping
3435 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3437 // put in calls to any functions which may want to know about the ping times from
3439 if(Game_mode & GM_STANDALONE_SERVER){
3440 std_update_player_ping(p);
3443 // mark his socket as still alive (extra precaution)
3444 psnet_mark_received(Net_players[lookup].reliable_socket);
3446 // otherwise, do any special processing
3448 // if we're in the join game state, see if this pong came from a server on our
3450 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3451 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3456 // send a ping packet
3457 void send_ping(net_addr *addr)
3459 unsigned char data[8];
3462 // build the header and send the packet
3463 BUILD_HEADER( PING );
3464 psnet_send(addr, &data[0], packet_size);
3467 // send a pong packet
3468 void send_pong(net_addr *addr)
3470 unsigned char data[8];
3473 // build the header and send the packet
3475 psnet_send(addr, &data[0], packet_size);
3478 // sent from host to master. give me the list of missions you have.
3479 // this will be used only in a standalone mode
3480 void send_mission_list_request( int what )
3482 ubyte data[MAX_PACKET_SIZE];
3485 // build the header and ask for a list of missions or campaigns (depending
3486 // on the 'what' flag).
3487 BUILD_HEADER(MISSION_REQUEST);
3489 multi_io_send_reliable(Net_player, data, packet_size);
3492 // maximum number of bytes that we can send in a mission items packet.
3493 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3495 // defines used to tell what type of packets are being sent
3496 #define MISSION_LIST_ITEMS 1
3497 #define CAMPAIGN_LIST_ITEMS 2
3499 // send an individual mission file item
3500 void send_mission_items( net_player *pl )
3502 ubyte data[MAX_PACKET_SIZE];
3507 BUILD_HEADER(MISSION_ITEM);
3509 // send the list of missions and campaigns avilable on the server. Stop when
3510 // reaching a certain maximum
3511 type = MISSION_LIST_ITEMS;
3513 for (i = 0; i < Multi_create_mission_count; i++ ) {
3517 ADD_STRING( Multi_create_mission_list[i].filename );
3518 ADD_STRING( Multi_create_mission_list[i].name );
3519 ADD_DATA( Multi_create_mission_list[i].flags );
3520 ADD_DATA( Multi_create_mission_list[i].max_players );
3521 ADD_DATA( Multi_create_mission_list[i].respawn );
3524 ADD_DATA( Multi_create_mission_list[i].valid_status );
3526 if ( packet_size > MAX_MISSION_ITEMS_BYTES ) {
3529 multi_io_send_reliable(pl, data, packet_size);
3530 BUILD_HEADER( MISSION_ITEM );
3536 multi_io_send_reliable(pl, data, packet_size);
3538 // send the campaign information
3539 type = CAMPAIGN_LIST_ITEMS;
3540 BUILD_HEADER(MISSION_ITEM);
3542 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3546 ADD_STRING( Multi_create_campaign_list[i].filename );
3547 ADD_STRING( Multi_create_campaign_list[i].name );
3548 ADD_DATA( Multi_create_campaign_list[i].flags );
3549 ADD_DATA( Multi_create_campaign_list[i].max_players );
3551 if ( packet_size > MAX_MISSION_ITEMS_BYTES ) {
3554 multi_io_send_reliable(pl, data, packet_size);
3555 BUILD_HEADER( MISSION_ITEM );
3561 multi_io_send_reliable(pl, data, packet_size);
3564 // process a request for a list of missions
3565 void process_mission_request_packet(ubyte *data, header *hinfo)
3567 int player_num,offset;
3569 offset = HEADER_LENGTH;
3572 // fill in the address information of where this came from
3573 player_num = find_player_id(hinfo->id);
3574 if(player_num == -1){
3575 nprintf(("Network","Could not find player to send mission list items to!\n"));
3579 send_mission_items( &Net_players[player_num] );
3582 // process an individual mission file item
3583 void process_mission_item_packet(ubyte *data,header *hinfo)
3586 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3587 ubyte stop, type,max_players;
3590 Assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3591 offset = HEADER_LENGTH;
3596 GET_STRING( filename );
3599 GET_DATA( max_players );
3601 // missions also have respawns and a crc32 associated with them
3602 if(type == MISSION_LIST_ITEMS){
3606 GET_DATA(valid_status);
3608 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3609 strcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename );
3610 strcpy(Multi_create_mission_list[Multi_create_mission_count].name, name );
3611 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3612 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3613 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3616 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3618 Multi_create_mission_count++;
3620 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3621 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3622 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename );
3623 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name );
3624 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3625 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3626 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3627 Multi_create_campaign_count++;
3636 // this will cause whatever list to get resorted (although they should be appearing in order)
3637 multi_create_setup_list_data(-1);
3640 // send a request to the server to pause or unpause the game
3641 void send_multi_pause_packet(int pause)
3643 ubyte data[MAX_PACKET_SIZE];
3645 int packet_size = 0;
3647 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3650 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3651 val = (ubyte) pause;
3653 // add the pause info
3656 // send the request to the server
3657 multi_io_send_reliable(Net_player, data, packet_size);
3660 // process a pause update packet (pause, unpause, etc)
3661 void process_multi_pause_packet(ubyte *data, header *hinfo)
3667 offset = HEADER_LENGTH;
3673 // get who sent the packet
3674 player_index = find_player_id(hinfo->id);
3675 // if we don't know who sent the packet, don't do anything
3676 if(player_index == -1){
3680 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3681 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3684 // send a game information update
3685 void send_game_info_packet()
3688 ubyte data[MAX_PACKET_SIZE], paused;
3690 // set the paused variable
3691 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3693 BUILD_HEADER(GAME_INFO);
3694 ADD_DATA( Missiontime );
3697 multi_io_send_to_all(data, packet_size);
3700 // process a game information update
3701 void process_game_info_packet( ubyte *data, header *hinfo )
3707 offset = HEADER_LENGTH;
3709 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3710 // time, set our time to server time (should take ping time into account!!!)
3711 GET_DATA( mission_time );
3716 // send an ingame nak packet
3717 void send_ingame_nak(int state, net_player *p)
3719 ubyte data[MAX_PACKET_SIZE];
3722 BUILD_HEADER(INGAME_NAK);
3726 multi_io_send_reliable(p, data, packet_size);
3729 // process an ingame nak packet
3730 void process_ingame_nak(ubyte *data, header *hinfo)
3732 int offset,state,pid;
3735 offset = HEADER_LENGTH;
3739 pid = find_player_id(hinfo->id);
3743 pl = &Net_players[pid];
3746 case ACK_FILE_ACCEPTED :
3747 Assert(Net_player->flags & NETINFO_FLAG_INGAME_JOIN);
3748 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3749 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3754 // send a packet telling players to end the mission
3755 void send_endgame_packet(net_player *pl)
3757 ubyte data[MAX_PACKET_SIZE];
3761 BUILD_HEADER(MISSION_END);
3763 // sending to a specific player?
3765 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3766 multi_io_send_reliable(pl, data, packet_size);
3770 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3771 // send all player stats here
3772 multi_broadcast_stats(STATS_MISSION);
3774 // if in dogfight mode, send all dogfight stats as well
3775 ml_string("Before dogfight stats!");
3776 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3777 ml_string("Sending dogfight stats!");
3779 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3781 ml_string("After dogfight stats!");
3783 // tell everyone to leave the game
3784 multi_io_send_to_all_reliable(data, packet_size);
3786 multi_io_send_reliable(Net_player, data, packet_size);
3790 // process a packet indicating we should end the current mission
3791 void process_endgame_packet(ubyte *data, header *hinfo)
3796 offset = HEADER_LENGTH;
3800 ml_string("Receiving endgame packet");
3802 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3803 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3804 // determine who this came from and make sure he is allowed to end the game
3805 player_num = find_player_id(hinfo->id);
3806 Assert(player_num != -1);
3811 // if the player is allowed to end the mission
3812 if(!multi_can_end_mission(&Net_players[player_num])){
3816 // act as if we hit alt+j locally
3817 multi_handle_end_mission_request();
3819 // all clients process immediately
3821 // ingame joiners should quit when they receive an endgame packet since the game is over
3822 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3823 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3827 // do any special processing for being in a state other than the gameplay state
3828 multi_handle_state_special();
3830 // make sure we're not already in the debrief state
3831 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3832 multi_warpout_all_players();
3837 // send a position/orientation update for myself (if I'm an observer)
3838 void send_observer_update_packet()
3840 ubyte data[MAX_PACKET_SIZE];
3845 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3846 // to send any update packets to anyone.
3847 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3851 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3857 BUILD_HEADER(OBSERVER_UPDATE);
3859 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3861 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3864 // add targeting infomation
3865 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3866 target_sig = Objects[Player_ai->target_objnum].net_signature;
3870 ADD_DATA(target_sig);
3872 multi_io_send(Net_player, data, packet_size);
3875 // process a position/orientation update from an observer
3876 void process_observer_update_packet(ubyte *data, header *hinfo)
3882 physics_info bogus_pi;
3885 offset = HEADER_LENGTH;
3887 obs_num = find_player_id(hinfo->id);
3889 memset(&bogus_pi,0,sizeof(physics_info));
3890 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
3892 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
3895 // targeting information
3896 GET_DATA(target_sig);
3899 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
3903 // set targeting info
3904 if(target_sig == 0){
3905 Net_players[obs_num].s_info.target_objnum = -1;
3907 target_obj = multi_get_network_object(target_sig);
3908 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
3911 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
3912 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
3913 Net_players[obs_num].s_info.eye_pos = g_vec;
3914 Net_players[obs_num].s_info.eye_orient = g_mat;
3917 void send_netplayer_slot_packet()
3919 ubyte data[MAX_PACKET_SIZE];
3920 int packet_size,idx;
3925 BUILD_HEADER(NETPLAYER_SLOTS_P);
3926 for(idx=0;idx<MAX_PLAYERS;idx++){
3927 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
3929 ADD_DATA(Net_players[idx].player_id);
3930 ADD_DATA(Objects[Net_players[idx].player->objnum].net_signature);
3931 ADD_DATA(Net_players[idx].p_info.ship_class);
3932 ADD_DATA(Net_players[idx].p_info.ship_index);
3938 // standalone case or not
3939 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3940 multi_io_send_to_all_reliable(data, packet_size);
3942 multi_io_send_reliable(Net_player, data, packet_size);
3946 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
3949 int player_num,ship_class,ship_index;
3955 offset = HEADER_LENGTH;
3957 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
3958 multi_untag_player_ships();
3962 GET_DATA(player_id);
3964 GET_DATA(ship_class);
3965 GET_DATA(ship_index);
3966 player_num = find_player_id(player_id);
3968 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
3970 // call the function in multiutil.cpp to set up the player object stuff
3971 // being careful not to muck with the standalone object
3972 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
3973 objp = multi_get_network_object(net_sig);
3974 Assert(objp != NULL);
3975 multi_assign_player_ship( player_num, objp, ship_class );
3976 Net_players[player_num].p_info.ship_index = ship_index;
3977 objp->flags &= ~(OF_COULD_BE_PLAYER);
3978 objp->flags |= OF_PLAYER_SHIP;
3985 // standalone should forward the packet and wait for a response
3986 if(Game_mode & GM_STANDALONE_SERVER){
3987 send_netplayer_slot_packet();
3990 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
3991 send_netplayer_update_packet();
3994 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
3995 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
3996 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
3997 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
3999 ubyte data[MAX_PACKET_SIZE], utmp;
4002 BUILD_HEADER(SHIP_WSTATE_CHANGE);
4003 ADD_DATA( Objects[shipp->objnum].net_signature );
4004 utmp = (ubyte)(what);
4006 utmp = (ubyte)(new_bank);
4008 utmp = (ubyte)(link_status);
4011 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
4012 multi_io_send_to_all_reliable(data, packet_size);
4015 void process_ship_weapon_change( ubyte *data, header *hinfo )
4019 ubyte what, new_bank, link_status;
4023 offset = HEADER_LENGTH;
4024 GET_DATA( signature );
4026 GET_DATA( new_bank );
4027 GET_DATA( link_status );
4030 objp = multi_get_network_object( signature );
4031 if ( objp == NULL ) {
4032 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4035 // Assert( objp->type == OBJ_SHIP );
4036 if(objp->type != OBJ_SHIP){
4040 // if this is my data, do nothing since I already have my own data
4041 if ( objp == Player_obj ){
4045 // now, get the ship and set the new bank and link modes based on the 'what' value
4046 shipp = &Ships[objp->instance];
4047 if ( what == MULTI_PRIMARY_CHANGED ) {
4048 shipp->weapons.current_primary_bank = new_bank;
4050 shipp->flags |= SF_PRIMARY_LINKED;
4052 shipp->flags &= ~SF_PRIMARY_LINKED;
4055 shipp->weapons.current_secondary_bank = new_bank;
4057 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4059 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4064 // ship status change procedure
4065 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4067 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4068 // for added redundancy.
4069 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4070 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4071 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4072 // back to the client. Also sends multiple times for redundancy
4073 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4074 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4075 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4077 #define SHIP_STATUS_REPEAT 2
4078 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4081 ubyte data[MAX_PACKET_SIZE];
4082 int packet_size = 0;
4088 BUILD_HEADER(SHIP_STATUS_CHANGE);
4090 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4091 temp = bi->status[idx];
4095 // server should send reliably (response packet)
4096 if(MULTIPLAYER_MASTER){
4097 multi_io_send_reliable(pl, data, packet_size);
4099 multi_io_send(pl, data, packet_size);
4103 void process_ship_status_packet(ubyte *data, header *hinfo)
4107 int player_num,unique_id;
4111 offset = HEADER_LENGTH;
4113 // zero out the button info structure for good measure
4114 memset(&bi,0,sizeof(button_info));
4116 // read the button-info
4117 GET_DATA(unique_id);
4119 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4121 bi.status[idx] = i_tmp;
4126 // this will be handled differently client and server side. Duh.
4127 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4128 // find which net-player has sent us butotn information
4129 player_num = find_player_id(hinfo->id);
4130 Assert(player_num >= 0);
4135 // don't process critical button information for observers
4136 // its a new button_info for this guy. apply and ack
4137 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4138 // mark that he's pressed this button
4139 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4141 // send a return packet
4142 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4144 // apply the button presses to his ship as normal
4145 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4147 // else ignore it as a repeat from the same guy
4148 } else { // CLIENT SIDE
4149 // this is the return from the server, so we should now apply them locally
4150 // if(lookup_ship_status(Net_player,unique_id,1)){
4151 multi_apply_ship_status(Net_player, &bi, 1);
4156 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4157 // for clients. Left all details to this function instead of higher level messaging
4159 void send_player_order_packet(int type, int index, int cmd)
4161 ubyte data[MAX_PACKET_SIZE];
4163 ushort target_signature;
4165 int packet_size = 0;
4167 BUILD_HEADER(PLAYER_ORDER_PACKET);
4170 ADD_DATA(val); // ship order or wing order, or message all fighters
4172 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4173 if ( val != SQUAD_MSG_ALL ){
4174 ADD_DATA(index); // net signature of target ship
4177 ADD_DATA(cmd); // the command itself
4180 target_signature = 0;
4181 if ( Player_ai->target_objnum != -1 ){
4182 target_signature = Objects[Player_ai->target_objnum].net_signature;
4185 ADD_DATA( target_signature );
4188 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4191 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4192 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
4193 t_subsys = (char)s_index;
4197 multi_io_send_reliable(Net_player, data, packet_size);
4200 // brief explanation :
4201 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4202 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4203 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4204 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4205 void process_player_order_packet(ubyte *data, header *hinfo)
4207 int offset, player_num, command, index = 0, tobjnum_save;
4208 ushort target_signature;
4209 char t_subsys, type;
4210 object *objp, *target_objp;
4213 ship_subsys *tsubsys_save, *targeted_subsys;
4215 Assert(MULTIPLAYER_MASTER);
4217 // packet values - its easier to read all of these in first
4219 offset = HEADER_LENGTH;
4222 if ( type != SQUAD_MSG_ALL ){
4226 GET_DATA( command );
4227 GET_DATA( target_signature );
4228 GET_DATA( t_subsys );
4232 player_num = find_player_id(hinfo->id);
4233 if(player_num == -1){
4234 nprintf(("Network","Received player order packet from unknown player\n"));
4238 objp = &Objects[Net_players[player_num].player->objnum];
4239 if ( objp->type != OBJ_SHIP ) {
4240 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4244 // HACK HACK HACK HACK HACK HACK
4245 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4246 // the data is BOGUS. All people should be able to to these things as well.
4247 if(command == REARM_REPAIR_ME_ITEM){
4248 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4250 } else if(command == ABORT_REARM_REPAIR_ITEM){
4251 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4255 // if this player is not allowed to do messaging, quit here
4256 if( !multi_can_message(&Net_players[player_num]) ){
4257 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4261 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4262 // then call them in.
4263 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4264 Assert( (index >= 0) && (index < Num_reinforcements) );
4265 hud_squadmsg_call_reinforcement(index, player_num);
4269 // set the player's ai information here
4270 shipp = &Ships[objp->instance];
4271 aip = &Ai_info[shipp->ai_index];
4273 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4274 target_objp = multi_get_network_object( target_signature );
4275 if ( target_objp == NULL ) {
4279 targeted_subsys = NULL;
4280 if ( t_subsys != -1 ) {
4281 Assert( target_objp != NULL );
4282 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4285 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4287 tobjnum_save = aip->target_objnum;
4288 tsubsys_save = aip->targeted_subsys;
4290 if ( target_objp ) {
4291 aip->target_objnum = OBJ_INDEX(target_objp);
4293 aip->target_objnum = -1;
4296 aip->targeted_subsys = targeted_subsys;
4298 if ( type == SQUAD_MSG_SHIP ) {
4299 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4300 } else if ( type == SQUAD_MSG_WING ) {
4301 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4302 } else if ( type == SQUAD_MSG_ALL ) {
4303 hud_squadmsg_send_to_all_fighters( command, player_num );
4306 Assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4307 aip->target_objnum = tobjnum_save;
4308 aip->targeted_subsys = tsubsys_save;
4311 // FILE SIGNATURE stuff :
4312 // there are 2 cases for file signature sending which are handled very differently
4313 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4314 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4315 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4316 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4317 // would have to keep track of up to potentially 14 other ack handles (ouch).
4318 void send_file_sig_packet(ushort sum_sig,int length_sig)
4320 ubyte data[MAX_PACKET_SIZE];
4321 int packet_size = 0;
4323 BUILD_HEADER(FILE_SIG_INFO);
4325 ADD_DATA(length_sig);
4327 multi_io_send_reliable(Net_player, data, packet_size);
4330 void process_file_sig_packet(ubyte *data, header *hinfo)
4335 offset = HEADER_LENGTH;
4337 // should only be received on the server-side
4338 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4341 GET_DATA(length_sig);
4343 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4346 void send_file_sig_request(char *file_name)
4348 ubyte data[MAX_PACKET_SIZE];
4349 int packet_size = 0;
4351 BUILD_HEADER(FILE_SIG_REQUEST);
4352 ADD_STRING(file_name);
4354 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4356 multi_io_send_to_all_reliable(data, packet_size);
4359 void process_file_sig_request(ubyte *data, header *hinfo)
4361 int offset = HEADER_LENGTH;
4363 // get the mission name
4364 GET_STRING(Netgame.mission_name);
4367 // set the current mission filename
4368 strcpy(Game_current_mission_filename,Netgame.mission_name);
4371 multi_get_mission_checksum(Game_current_mission_filename);
4373 if(!multi_endgame_ending()){
4374 // reply to the server
4375 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4379 // functions to deal with subsystems getting whacked
4380 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4382 ubyte data[MAX_PACKET_SIZE];
4385 vector tmp, local_hitpos;
4388 Assert ( index < UCHAR_MAX );
4389 uindex = (ubyte)(index);
4391 objp = &Objects[shipp->objnum];
4393 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4394 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4396 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4397 ADD_DATA( Objects[shipp->objnum].net_signature );
4399 ADD_DATA( local_hitpos );
4401 multi_io_send_to_all_reliable(data, packet_size);
4404 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4410 vector local_hit_pos, world_hit_pos;
4412 offset = HEADER_LENGTH;
4414 GET_DATA( signature );
4416 GET_DATA( local_hit_pos );
4418 // get the network object. process it if we find it.
4419 objp = multi_get_network_object( signature );
4420 if ( objp != NULL ) {
4422 ship_subsys *subsysp;
4424 // be sure we have a ship!!!
4425 // Assert ( objp->type == OBJ_SHIP );
4426 if(objp->type != OBJ_SHIP){
4431 shipp = &Ships[objp->instance];
4433 // call to get the pointer to the subsystem we should be working on
4434 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4435 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4436 vm_vec_add2( &world_hit_pos, &objp->pos );
4438 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4439 if ( objp == Player_obj ) {
4440 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4448 // packet to tell clients cargo of a ship was revealed to all
4449 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4451 ubyte data[MAX_PACKET_SIZE], uindex;
4454 Assert ( index < UCHAR_MAX );
4455 uindex = (ubyte)(index);
4457 // build the header and add the data
4458 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4459 ADD_DATA( Objects[shipp->objnum].net_signature );
4462 // server sends to all players
4463 if(MULTIPLAYER_MASTER){
4464 multi_io_send_to_all_reliable(data, packet_size);
4466 // clients just send to the server
4468 multi_io_send_reliable(Net_player, data, packet_size);
4472 // process a subsystem cargo revealed packet
4473 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4480 ship_subsys *subsysp;
4482 offset = HEADER_LENGTH;
4483 GET_DATA( signature );
4487 // get a ship pointer and call the ship function to reveal the cargo
4488 objp = multi_get_network_object( signature );
4489 if ( objp == NULL ) {
4490 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4494 // Assert( objp->type == OBJ_SHIP );
4495 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4499 shipp = &Ships[objp->instance];
4501 // call to get the pointer to the subsystem we should be working on
4502 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4503 if (subsysp == NULL) {
4504 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4508 // this will take care of re-routing to all other clients
4509 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4510 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4512 // server should rebroadcast
4513 if(MULTIPLAYER_MASTER){
4514 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4518 void send_netplayer_load_packet(net_player *pl)
4520 ubyte data[MAX_PACKET_SIZE];
4521 int packet_size = 0;
4523 BUILD_HEADER(LOAD_MISSION_NOW);
4524 ADD_STRING(Netgame.mission_name);
4527 multi_io_send_to_all_reliable(data, packet_size);
4529 multi_io_send_reliable(pl, data, packet_size);
4533 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4536 int offset = HEADER_LENGTH;
4541 strcpy(Netgame.mission_name,str);
4542 strcpy(Game_current_mission_filename,str);
4543 if(!Multi_mission_loaded){
4545 // MWA 2/3/98 -- ingame join changes!!!
4546 // everyone can go through the same mission loading path here!!!!
4547 nprintf(("Network","Loading mission..."));
4549 // notify everyone that I'm loading the mission
4550 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4551 send_netplayer_update_packet();
4553 // do the load itself
4554 game_start_mission();
4556 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4557 // code will remark the correct player ships
4558 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4559 multi_untag_player_ships();
4562 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4563 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4564 send_netplayer_update_packet();
4566 Multi_mission_loaded = 1;
4567 nprintf(("Network","Finished loading mission\n"));
4571 void send_jump_into_mission_packet(net_player *pl)
4573 ubyte data[MAX_PACKET_SIZE];
4574 int packet_size = 0;
4576 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4578 BUILD_HEADER(JUMP_INTO_GAME);
4580 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4581 // and possible other things.
4583 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4584 ADD_DATA(Netgame.game_state);
4590 multi_io_send_to_all_reliable(data, packet_size);
4592 // send to a specific player
4594 multi_io_send_reliable(pl, data, packet_size);
4598 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4600 int offset = HEADER_LENGTH;
4605 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4606 // the game could be paused, so ingame joiner needs to deal with it.
4607 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4609 Netgame.game_state = state;
4614 // handle any special processing for being in a weird substate
4615 multi_handle_state_special();
4617 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4618 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4619 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4620 multi_ingame_observer_finish();
4622 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4623 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4624 send_netplayer_update_packet();
4627 // start the mission!!
4628 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4629 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4630 gameseq_post_event(GS_EVENT_ENTER_GAME);
4631 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4632 send_netplayer_update_packet();
4636 extern int Player_multi_died_check;
4637 Player_multi_died_check = -1;
4639 // recalc all object pairs now
4640 extern void obj_reset_all_collisions();
4641 obj_reset_all_collisions();
4643 // display some cool text
4644 multi_common_add_text(XSTR("Received mission start\n",720),1);
4647 ml_string(NOX("Client received mission start from server - entering mission"));
4652 char *repair_text[] = {
4654 "REPAIR_INFO_BEGIN",
4656 "REPAIR_INFO_UPDATE",
4657 "REPAIR_INFO_QUEUE",
4658 "REPAIR_INFO_ABORT",
4659 "REPAIR_INFO_BROKEN",
4660 "REPAIR_INFO_WARP_ADD",
4661 "REPAIR_INFO_WARP_REMOVE",
4662 "REPAIR_INFO_ONWAY",
4663 "REPAIR_INFO_KILLED",
4664 "REPAIR_INFO_COMPLETE",
4669 // the following two routines deal with updating and sending information regarding players
4670 // rearming and repairing during the game. The process function calls the routines to deal with
4671 // setting flags and other interesting things.
4672 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4674 int packet_size = 0;
4675 ushort repaired_signature, repair_signature;
4676 ubyte data[MAX_PACKET_SIZE];
4679 // use the network signature of the destination object if there is one, -1 otherwise.
4680 // client will piece it all together
4681 repaired_signature = repaired_objp->net_signature;
4683 // the repair ship may be NULL here since it might have been destroyed
4684 repair_signature = 0;
4686 repair_signature = repair_objp->net_signature;
4689 BUILD_HEADER(CLIENT_REPAIR_INFO);
4692 ADD_DATA( repaired_signature );
4693 ADD_DATA( repair_signature );
4695 multi_io_send_to_all_reliable(data, packet_size);
4697 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));
4700 void process_repair_info_packet(ubyte *data, header *hinfo)
4702 int offset = HEADER_LENGTH;
4703 ushort repaired_signature, repair_signature;
4704 object *repaired_objp, *repair_objp;
4708 GET_DATA( repaired_signature );
4709 GET_DATA( repair_signature );
4712 repaired_objp = multi_get_network_object( repaired_signature );
4713 repair_objp = multi_get_network_object( repair_signature );
4715 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));
4717 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4721 if ( repaired_objp == NULL ) {
4722 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4726 // the hope is to simply call the routine in the ai code to set/unset flags
4727 // based on the code value and everything else should happen..I hope....
4728 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4730 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4732 // set the dock flags when repair begins. Prevents problem in lagging docking
4733 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4735 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4736 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4737 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4740 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4742 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4743 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4746 if ( code == REPAIR_INFO_WARP_ADD ){
4747 mission_warp_in_support_ship( repaired_objp );
4749 mission_remove_scheduled_repair( repaired_objp );
4754 // sends information updating clients on certain AI information that clients will
4755 // need to know about to keep HUD information up to date. objp is the object that we
4756 // are updating, and what is the type of stuff that we are updating.
4757 void send_ai_info_update_packet( object *objp, char what )
4760 ushort other_signature;
4761 ubyte data[MAX_PACKET_SIZE];
4763 ubyte dock_index, dockee_index;
4765 // Assert( objp->type == OBJ_SHIP );
4766 if(objp->type != OBJ_SHIP){
4769 aip = &Ai_info[Ships[objp->instance].ai_index];
4772 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4775 BUILD_HEADER( AI_INFO_UPDATE );
4776 ADD_DATA( objp->net_signature );
4779 // depending on the "what" value, we will send different information
4783 case AI_UPDATE_DOCK:
4784 // for docking ships, add the signature of the ship that we are docking with.
4785 Assert( aip->dock_objnum != -1 );
4786 other_signature = Objects[aip->dock_objnum].net_signature;
4787 dock_index = (ubyte)(aip->dock_index);
4788 dockee_index = (ubyte)(aip->dockee_index);
4789 ADD_DATA( other_signature );
4790 ADD_DATA(dock_index);
4791 ADD_DATA(dockee_index);
4794 case AI_UPDATE_UNDOCK:
4795 // for undocking ships, check the dock_objnum since we might or might not have it
4796 // depending on whether or not a ship was destroyed while we were docked.
4797 other_signature = 0;
4798 if ( aip->dock_objnum != -1 )
4799 other_signature = Objects[aip->dock_objnum].net_signature;
4800 ADD_DATA( other_signature );
4804 case AI_UPDATE_ORDERS: {
4807 // for orders, we only need to send a little bit of information here. Be sure that the
4808 // first order for this ship is active
4809 Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4810 ADD_DATA( aip->goals[0].ai_mode );
4811 ADD_DATA( aip->goals[0].ai_submode );
4813 if ( aip->goals[0].ship_name != NULL )
4814 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4816 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4817 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4818 if ( shipnum != -1 ) {
4819 Assert( Ships[shipnum].objnum != -1 );
4820 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4822 other_signature = 0;
4824 ADD_DATA( other_signature );
4826 // for docking, add the dock and dockee index
4827 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4828 Assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4829 Assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4830 dock_index = (ubyte)aip->goals[0].docker.index;
4831 dockee_index = (ubyte)aip->goals[0].dockee.index;
4832 ADD_DATA( dock_index );
4833 ADD_DATA( dockee_index );
4842 multi_rate_add(1, "aiu", packet_size);
4843 multi_io_send_to_all_reliable(data, packet_size);
4846 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4847 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4848 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4850 int offset = HEADER_LENGTH;
4852 ushort net_signature, other_net_signature;
4853 object *objp, *other_objp;
4856 ubyte dock_index = 0, dockee_index = 0;
4858 GET_DATA( net_signature ); // signature of the object that we are dealing with.
4859 GET_DATA( code ); // code of what we are doing.
4860 objp = multi_get_network_object( net_signature );
4862 nprintf(("Network", "Couldn't find object for ai update\n"));
4865 case AI_UPDATE_DOCK:
4866 GET_DATA( other_net_signature );
4867 GET_DATA( dock_index );
4868 GET_DATA( dockee_index );
4869 other_objp = multi_get_network_object( other_net_signature );
4871 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
4873 // if we don't have an object to work with, break out of loop
4874 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
4878 Assert( other_objp->type == OBJ_SHIP );
4879 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
4880 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
4882 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4883 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4885 ai_do_objects_docked_stuff( objp, other_objp );
4888 case AI_UPDATE_UNDOCK:
4889 GET_DATA( other_net_signature );
4890 other_objp = multi_get_network_object( other_net_signature );
4892 // if we don't have an object to work with, break out of loop
4896 ai_do_objects_undocked_stuff( objp, other_objp );
4899 case AI_UPDATE_ORDERS:
4901 GET_DATA( submode );
4902 GET_DATA( other_net_signature );
4903 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4904 GET_DATA(dock_index);
4905 GET_DATA(dockee_index);
4908 // be sure that we have a ship object!!!
4909 if ( !objp || (objp->type != OBJ_SHIP) )
4912 // set up the information in the first goal element of the object in question
4913 aip = &Ai_info[Ships[objp->instance].ai_index];
4914 aip->active_goal = 0;
4915 aip->goals[0].ai_mode = mode;
4916 aip->goals[0].ai_submode = submode;
4918 // for docking, add the dock and dockee index
4919 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4920 aip->dock_index = dock_index;
4921 aip->dockee_index = dockee_index;
4924 // get a shipname if we can.
4925 other_objp = multi_get_network_object( other_net_signature );
4926 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
4927 // get a pointer to the shipname in question. Use the ship_name value in the
4928 // ship. We are only using this for HUD display, so I think that using this
4929 // method will be fine.
4930 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
4932 // special case for destroy subsystem -- get the ai_info pointer to our target ship
4933 // so that we can properly set up what subsystem this ship is attacking.
4934 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
4935 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
4937 // if docking -- set the dock index and dockee index of this other ship
4938 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4939 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4940 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4947 Int3(); // this Int3() should be temporary
4948 nprintf(("Network", "Invalid code for ai update: %d\n", code));
4954 // tell the standalone to move into the MISSION_SYNC_STATE
4955 void send_mission_sync_packet(int mode,int start_campaign)
4957 ubyte data[MAX_PACKET_SIZE],is_campaign;
4958 int packet_size = 0;
4960 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
4962 // build the header and add the sync mode (pre or post briefing)
4963 BUILD_HEADER(MISSION_SYNC_DATA);
4966 // if this is a campaign game
4967 if(mode == MULTI_SYNC_PRE_BRIEFING){
4968 if(Game_mode & GM_CAMPAIGN_MODE){
4969 // add a byte indicating campaign mode
4971 ADD_DATA(is_campaign);
4973 // add a byte indicating if we should be starting a campaign or continuing it
4974 is_campaign = (ubyte)start_campaign;
4975 ADD_DATA(is_campaign);
4977 // add the campaign filename
4978 ADD_STRING(Netgame.campaign_name);
4980 // otherwise if this is a single mission
4982 // add a byte indicating single mission mode
4984 ADD_DATA(is_campaign);
4986 // add the mission filename
4987 ADD_STRING(Game_current_mission_filename);
4990 multi_io_send_reliable(Net_player, data, packet_size);
4993 // move into the MISSION_SYNC state when this is received
4994 // this packet is sent only from a game host to a standalone
4995 void process_mission_sync_packet(ubyte *data, header *hinfo)
4998 ubyte campaign_flag;
4999 int offset = HEADER_LENGTH;
5001 Assert(Game_mode & GM_STANDALONE_SERVER);
5003 // if this is a team vs team situation, lock the players send a final team update
5004 if(Netgame.type_flags & NG_TYPE_TEAM){
5005 multi_team_host_lock_all();
5006 multi_team_send_update();
5009 // get the sync mode (pre or post briefing)
5012 if(mode == MULTI_SYNC_PRE_BRIEFING){
5013 // get the flag indicating if this is a single mission or a campaign mode
5014 GET_DATA(campaign_flag);
5016 // get the flag indicating whether we should be starting a new campaign
5017 GET_DATA(campaign_flag);
5019 // get the campaign filename
5020 GET_STRING(Netgame.campaign_name);
5022 // either start a new campaign or continue on to the next mission in the current campaign
5024 multi_campaign_start(Netgame.campaign_name);
5026 multi_campaign_next_mission();
5029 // make sure we remove the campaign mode flag
5030 Game_mode &= ~(GM_CAMPAIGN_MODE);
5032 // get the single mission filename
5033 GET_STRING(Game_current_mission_filename);
5034 strcpy(Netgame.mission_name,Game_current_mission_filename);
5039 // set the correct mode and m ove into the state
5040 Multi_sync_mode = mode;
5041 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5044 // tell a player to merge his mission stats into his alltime stats
5045 void send_store_stats_packet(int accept)
5048 int packet_size = 0;
5050 BUILD_HEADER(STORE_MISSION_STATS);
5052 // add whether we're accepting or tossing
5053 val = (ubyte)accept;
5056 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5057 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5058 multi_io_send_to_all_reliable(data, packet_size);
5060 multi_io_send_reliable(Net_player, data, packet_size);
5064 void process_store_stats_packet(ubyte *data, header *hinfo)
5066 int offset = HEADER_LENGTH;
5072 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5073 if(Game_mode & GM_STANDALONE_SERVER){
5074 // rebroadcast the packet to all others in the game
5075 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5076 multi_io_send_to_all_reliable(data, offset);
5079 // all players should mark the stats as being accepted in the debriefing
5080 multi_debrief_stats_accept();
5082 // all players should mark the stats as being "tossed" in the debriefing
5083 multi_debrief_stats_toss();
5088 void send_debris_update_packet(object *objp,int code)
5090 ubyte data[MAX_PACKET_SIZE];
5092 int packet_size = 0;
5094 BUILD_HEADER(DEBRIS_UPDATE);
5095 ADD_DATA(objp->net_signature);
5099 // add any extra relevant data
5101 case DEBRIS_UPDATE_UPDATE:
5102 ADD_DATA(objp->pos); // add position
5103 ADD_ORIENT(objp->orient); // add orientation
5104 ADD_DATA(objp->phys_info.vel); // add velocity
5105 ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5108 multi_io_send_to_all(data, packet_size);
5111 void process_debris_update_packet(ubyte *data, header *hinfo)
5115 object bogus_object;
5117 int offset = HEADER_LENGTH;
5123 objp = multi_get_network_object(net_sig);
5125 objp = &bogus_object;
5129 // update the object
5130 case DEBRIS_UPDATE_UPDATE:
5131 GET_DATA(objp->pos);
5132 GET_ORIENT(objp->orient);
5133 GET_DATA(objp->phys_info.vel);
5134 GET_DATA(objp->phys_info.rotvel);
5136 // simply remove it (no explosion)
5137 case DEBRIS_UPDATE_REMOVE:
5138 if(objp != &bogus_object){
5139 Assert(objp->type == OBJ_DEBRIS);
5140 obj_delete(OBJ_INDEX(objp));
5144 case DEBRIS_UPDATE_NUKE:
5145 if(objp != &bogus_object)
5146 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5154 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)
5156 ubyte data[MAX_PACKET_SIZE];
5158 int packet_size = 0;
5160 BUILD_HEADER(WSS_REQUEST_PACKET);
5162 // add the request information
5163 ADD_DATA(player_id);
5164 ADD_DATA(from_slot);
5165 ADD_DATA(from_index);
5168 ADD_DATA(wl_ship_slot); // only used in weapons loadout
5169 ADD_DATA(ship_class);
5172 // a standard request
5174 multi_io_send_reliable(Net_player, data, packet_size);
5176 // being routed through the standalone to the host of the game
5178 Assert(Game_mode & GM_STANDALONE_SERVER);
5179 multi_io_send_reliable(p, data, packet_size);
5183 void process_wss_request_packet(ubyte *data, header *hinfo)
5185 int offset = HEADER_LENGTH;
5186 int from_slot,from_index;
5187 int to_slot,to_index;
5189 int wl_ship_slot,ship_class;
5193 // determine who this request is from
5194 GET_DATA(player_id);
5195 player_num = find_player_id(player_id);
5197 // read in the request data
5198 GET_DATA(from_slot);
5199 GET_DATA(from_index);
5202 GET_DATA(wl_ship_slot); // only used in weapons loadout
5203 GET_DATA(ship_class); // only used in multi team select
5207 Assert(player_num != -1);
5208 if(player_num == -1){
5212 // if we're the standalone, we have to route this packet to the host of the game
5213 if(Game_mode & GM_STANDALONE_SERVER){
5214 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5216 // otherwise we're the host and should process the request
5219 case WSS_WEAPON_SELECT :
5220 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5222 case WSS_SHIP_SELECT :
5223 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5231 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5233 ubyte data[MAX_PACKET_SIZE],team;
5234 int packet_size = 0;
5236 Assert(size <= (MAX_PACKET_SIZE - 10));
5238 BUILD_HEADER(WSS_UPDATE_PACKET);
5240 // add the team/pool # this is for
5241 team = (ubyte)team_num;
5244 // add the data block size
5247 // add the data itself
5248 memcpy(data + packet_size,wss_data,size);
5249 packet_size += size;
5251 // if we're also the master of the game (not on a standalone)
5252 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5253 multi_io_send_to_all_reliable(data, packet_size);
5255 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5257 multi_io_send_reliable(Net_player, data, packet_size);
5261 void process_wss_update_packet(ubyte *data, header *hinfo)
5264 int size,player_index,idx;
5265 int offset = HEADER_LENGTH;
5267 // get the team/pool #
5270 // get the data size
5273 // if we're the standalone, then we should be routing this data to all the other clients
5274 if(Game_mode & GM_STANDALONE_SERVER){
5279 // determine where this came from
5280 player_index = find_player_id(hinfo->id);
5281 Assert(player_index != -1);
5282 if(player_index < 0){
5286 // route the packet (don't resend it to the host)
5287 for(idx=0;idx<MAX_PLAYERS;idx++){
5288 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5289 multi_io_send_reliable(&Net_players[idx], data, offset);
5293 // set the proper pool pointers
5294 ss_set_team_pointers((int)team);
5296 // read in the block of data, and apply it to the weapons/ship pools
5297 offset += restore_wss_data(data + offset);
5300 // set the pool pointers back to my own team
5301 ss_set_team_pointers(Net_player->p_info.team);
5303 // sync the interface if this was for my own team
5304 if((int)team == Net_player->p_info.team){
5305 multi_ts_sync_interface();
5312 // function to send firing information from the client to the server once they reach
5313 // the final sync screen.
5314 void send_firing_info_packet()
5316 ubyte data[MAX_PACKET_SIZE];
5318 ubyte plinked, sdual;
5320 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5322 BUILD_HEADER(FIRING_INFO);
5323 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5324 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5325 ADD_DATA( plinked );
5328 multi_io_send_reliable(Net_player, data, packet_size);
5331 void process_firing_info_packet( ubyte *data, header *hinfo )
5333 int offset, player_num;
5334 ubyte plinked, sdual;
5337 // only the master of the game should be dealing with these packets
5338 Assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5340 offset = HEADER_LENGTH;
5341 GET_DATA( plinked );
5345 player_num = find_player_id(hinfo->id);
5347 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5351 // get the ship pointer for this player and set the flags accordingly.
5352 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5354 shipp->flags |= SF_PRIMARY_LINKED;
5356 shipp->flags &= ~SF_PRIMARY_LINKED;
5359 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5361 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5364 // packet to deal with changing status of mission goals. used to be sent every so often from server
5365 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5366 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5367 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5368 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5370 ubyte data[MAX_PACKET_SIZE];
5373 BUILD_HEADER(MISSION_GOAL_INFO);
5376 ADD_DATA(new_status);
5379 multi_io_send_to_all_reliable(data, packet_size);
5382 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5384 int offset, goal_num, new_status, valid;
5386 offset = HEADER_LENGTH;
5388 GET_DATA(new_status);
5392 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5393 if ( new_status != -1 ){
5394 mission_goal_status_change( goal_num, new_status );
5396 mission_goal_validation_change( goal_num, valid );
5400 void send_player_settings_packet(net_player *p)
5402 ubyte data[MAX_PACKET_SIZE];
5405 int packet_size = 0;
5408 BUILD_HEADER(PLAYER_SETTINGS);
5410 // add all the data for all the players
5412 for(idx=0;idx<MAX_PLAYERS;idx++){
5413 if(MULTI_CONNECTED(Net_players[idx])){
5415 ADD_DATA(Net_players[idx].player_id);
5417 // break the p_info structure by member, so we don't overwrite any absolute pointers
5418 // ADD_DATA(Net_players[idx].p_info);
5419 ADD_DATA(Net_players[idx].p_info.team);
5420 ADD_DATA(Net_players[idx].p_info.ship_index);
5421 ADD_DATA(Net_players[idx].p_info.ship_class);
5424 // add the stop byte
5428 // either broadcast the data or send to a specific player
5430 multi_io_send_to_all_reliable(data, packet_size);
5432 multi_io_send_reliable(p, data, packet_size);
5436 void process_player_settings_packet(ubyte *data, header *hinfo)
5438 int offset,player_num;
5439 net_player_info bogus,*ptr;
5443 offset = HEADER_LENGTH;
5445 // read in the data for all the players
5447 while(stop != 0xff){
5448 // lookup the player
5449 GET_DATA(player_id);
5450 player_num = find_player_id(player_id);
5452 // make sure this is a valid player
5453 if(player_num == -1){
5456 ptr = &Net_players[player_num].p_info;
5459 GET_DATA(ptr->team);
5460 GET_DATA(ptr->ship_index);
5461 GET_DATA(ptr->ship_class);
5468 // update the server with my new state
5469 // MWA -- 3/31/98 -- check for in mission instead of state.
5470 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5471 if( !(Game_mode & GM_IN_MISSION) ) {
5472 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5473 send_netplayer_update_packet();
5477 // display some cool text
5478 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5481 void send_deny_packet(net_addr *addr, int code)
5484 int packet_size = 0;
5486 // build the header and add the rejection code
5492 psnet_send(addr, data, packet_size);
5495 void process_deny_packet(ubyte *data, header *hinfo)
5499 // get the denial code
5500 offset = HEADER_LENGTH;
5504 // if there is already a dialog active, do nothing - who cares at this point.
5509 // display the appropriate dialog
5511 case JOIN_DENY_JR_STATE :
5512 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));
5514 case JOIN_DENY_JR_TRACKER_INVAL :
5515 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));
5517 case JOIN_DENY_JR_PASSWD :
5518 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5520 case JOIN_DENY_JR_CLOSED :
5521 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));
5523 case JOIN_DENY_JR_TEMP_CLOSED :
5524 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));
5526 case JOIN_DENY_JR_RANK_HIGH :
5527 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));
5529 case JOIN_DENY_JR_RANK_LOW :
5530 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));
5532 case JOIN_DENY_JR_DUP :
5533 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5535 case JOIN_DENY_JR_FULL :
5536 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5538 case JOIN_DENY_JR_BANNED :
5539 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5541 case JOIN_DENY_JR_NOOBS :
5542 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5544 case JOIN_DENY_JR_INGAME_JOIN :
5545 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));
5547 case JOIN_DENY_JR_BAD_VERSION :
5548 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));
5550 case JOIN_DENY_JR_TYPE :
5551 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5555 // call this so that the join request timestamp automatically expires when we hear back from the server
5556 multi_join_reset_join_stamp();
5559 // this packet will consist of
5560 // 1.) netplayer ship classes (85 bytes max)
5561 // 2.) ship weapon state data (241 bytes max)
5562 // 3.) player settings et. al. (133 bytes max)
5563 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5564 void send_post_sync_data_packet(net_player *p, int std_request)
5566 ubyte data[MAX_PACKET_SIZE], val;
5571 ushort sval, ship_ets;
5572 int idx, player_index;
5573 int packet_size = 0;
5577 BUILD_HEADER(POST_SYNC_DATA);
5579 // some header information for standalone packet routing purposes
5580 val = (ubyte)std_request;
5583 // the standalone has two situations
5584 // 1.) sending a request to the host to distribute this block of data
5585 // 2.) having recevied this block of data from the host, it redistributes it
5586 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5587 // case 1, send the request
5588 multi_io_send_reliable(Netgame.host, data, packet_size);
5591 // case 2 for the standalone is below (as normal)
5593 // otherwise build the data now
5595 // add all deleted ships
5596 val = (ubyte)Multi_ts_num_deleted;
5598 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5599 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5605 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5606 shipp = &Ships[Objects[so->objnum].instance];
5608 // don't process non player wing ships
5609 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5615 // # of ships - used multiple times in the packet
5616 val = (ubyte)ship_count;
5619 // add ship class information (85 bytes max)
5620 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5621 shipp = &Ships[Objects[so->objnum].instance];
5623 // don't process non player wing ships
5624 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5627 // add the net signature of the object for look up
5628 ADD_DATA( Objects[so->objnum].net_signature );
5630 // add the ship info index
5631 val = (ubyte)(shipp->ship_info_index);
5634 // add the ships's team select index
5635 val = (ubyte)shipp->ts_index;
5639 // add weapon state information for all starting ships (241 bytes max)
5640 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5641 shipp = &Ships[Objects[so->objnum].instance];
5643 // don't process non player wing ships
5644 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5647 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5649 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5650 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5651 if(player_index == -1){
5654 pl = &Net_players[player_index];
5658 // add the net signature and other weapon information
5659 ADD_DATA( Objects[so->objnum].net_signature );
5661 // add number of primary and secondary banks
5662 bval = (char)(shipp->weapons.num_primary_banks);
5664 bval = (char)(shipp->weapons.num_secondary_banks);
5667 // add weapon bank status
5668 bval = (char)(shipp->weapons.current_primary_bank);
5670 pl->s_info.cur_primary_bank = bval;
5672 // Assert(bval != -1);
5675 bval = (char)(shipp->weapons.current_secondary_bank);
5677 pl->s_info.cur_secondary_bank = bval;
5679 // Assert(bval != -1);
5682 // primary weapon info
5683 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5685 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5688 // secondary weapon info
5689 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5691 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5692 ADD_DATA(val_short);
5693 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5695 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5696 ADD_DATA(val_short);
5697 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5699 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5700 ADD_DATA(val_short);
5702 // send primary and secondary weapon link status
5704 if(shipp->flags & SF_PRIMARY_LINKED){
5706 pl->s_info.cur_link_status |= (1<<0);
5710 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5712 pl->s_info.cur_link_status |= (1<<1);
5716 // if this is a player ship add (1<<2)
5717 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5722 // add a ship ets value
5725 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5727 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5729 ship_ets |= ((ushort)shipp->engine_recharge_index);
5734 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5735 // or if I'm the server as well as the host, I should be sending this to all players
5736 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5737 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5740 multi_io_send_to_all_reliable(data, packet_size);
5742 // send to a specific player
5744 multi_io_send_reliable(p, data, packet_size);
5747 multi_io_send_reliable(Net_player, data, packet_size);
5754 multi_io_send_to_all_reliable(data, packet_size);
5756 // send to a specific player
5758 multi_io_send_reliable(p, data, packet_size);
5763 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5765 ubyte val, sinfo_index, ts_index;
5767 ushort net_sig, ship_ets, sval;
5771 int offset = HEADER_LENGTH;
5775 // packet routing information
5778 // 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
5779 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5782 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5783 multi_ts_create_wings();
5785 // send to the standalone through my socket
5786 send_post_sync_data_packet(Net_player);
5792 // add all deleted ships
5794 Multi_ts_num_deleted = (int)val;
5795 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5796 // get the ship's objnum
5799 objp = multi_get_network_object(sval);
5801 // delete the ship appropriately
5802 // mark the object as having been deleted
5803 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5806 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5807 obj_delete(Multi_ts_deleted_objnums[idx]);
5808 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5810 Multi_ts_num_deleted--;
5811 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5819 // process ship class information
5820 for(idx=0; idx<ship_count; idx++){
5821 // get the object's net signature
5823 GET_DATA(sinfo_index);
5826 // attempt to get the object
5827 objp = multi_get_network_object(net_sig);
5829 // make sure we found a ship
5830 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5832 // set the ship to be the right class
5833 change_ship_type(objp->instance,(int)sinfo_index);
5835 // set the ship's team select index
5836 Ships[objp->instance].ts_index = (int)ts_index;
5839 // process ship weapon state info
5840 for(idx=0; idx<ship_count; idx++){
5841 // get the object's net signature
5844 // attempt to get the object
5845 objp = multi_get_network_object(net_sig);
5847 // make sure we found a ship
5848 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5850 // get a pointer to the ship
5851 shipp = &Ships[objp->instance];
5853 // get number of primary and secondary banks;
5856 shipp->weapons.num_primary_banks = (int)b;
5860 shipp->weapons.num_secondary_banks = (int)b;
5862 // get bank selection info
5867 shipp->weapons.current_primary_bank = (int)b;
5873 shipp->weapons.current_secondary_bank = (int)b;
5875 // primary weapon info
5877 shipp->weapons.primary_bank_weapons[0] = (int)b;
5880 shipp->weapons.primary_bank_weapons[1] = (int)b;
5882 // secondary weapon info
5884 shipp->weapons.secondary_bank_weapons[0] = (int)b;
5885 GET_DATA(val_short);
5886 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
5889 shipp->weapons.secondary_bank_weapons[1] = (int)b;
5890 GET_DATA(val_short);
5891 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
5894 shipp->weapons.secondary_bank_weapons[2] = (int)b;
5895 GET_DATA(val_short);
5896 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
5903 shipp->flags |= SF_PRIMARY_LINKED;
5906 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5908 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
5909 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
5911 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
5913 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
5919 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
5921 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
5923 shipp->engine_recharge_index = (ship_ets & 0x000f);
5928 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
5929 send_netplayer_update_packet();
5931 // the standalone server will receive this packet from the host of the game, to be applied locally and
5932 // also to be rebroadcast.
5933 if(Game_mode & GM_STANDALONE_SERVER){
5934 // update player ets settings
5935 for(idx=0;idx<MAX_PLAYERS;idx++){
5936 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
5937 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
5941 send_post_sync_data_packet(NULL,0);
5945 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
5947 ubyte data[MAX_PACKET_SIZE],val;
5950 int packet_size = 0;
5953 BUILD_HEADER(WSS_SLOTS_DATA);
5955 // some header information for standalone packet routing purposes
5956 val = (ubyte)std_request;
5960 val = (ubyte)team_num;
5963 // add whether this is the final packet or not
5967 // the standalone has two situations
5968 // 1.) sending a request to the host to distribute this block of data
5969 // 2.) having recevied this block of data from the host, it redistributes it
5970 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
5971 // case 1, send the request
5972 multi_io_send_reliable(Netgame.host, data, packet_size);
5975 // case 2 for the standalone is below (as normal)
5977 // add all the slots
5978 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
5979 // add the ship class
5980 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
5984 for(i = 0;i<MAX_WL_WEAPONS;i++){
5985 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
5989 // add the weapon counts
5990 for(i = 0;i<MAX_WL_WEAPONS;i++){
5991 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
5992 ADD_DATA(val_short);
5996 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5997 // or if I'm the server as well as the host, I should be sending this to all players
5998 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5999 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6002 multi_io_send_to_all_reliable(data, packet_size);
6004 // send to a specific player
6006 multi_io_send_reliable(p, data, packet_size);
6009 multi_io_send_reliable(Net_player, data, packet_size);
6016 multi_io_send_to_all_reliable(data, packet_size);
6018 // send to a specific player
6020 multi_io_send_reliable(p, data, packet_size);
6025 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6027 ubyte val,team_num,final;
6029 int offset = HEADER_LENGTH;
6032 // packet routing information
6038 // get whether this is the final packet or not
6041 // 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
6042 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6045 // send to the standalone through my socket
6046 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6050 // read in all the slot data
6051 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6052 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6054 // get the ship class
6056 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6059 for(i = 0;i<MAX_WL_WEAPONS;i++){
6061 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6063 // check for signed/unsigned problems
6064 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6065 Wss_slots_teams[team_num][idx].wep[i] = -1;
6069 // get the weapon counts
6070 for(i = 0;i<MAX_WL_WEAPONS;i++){
6071 GET_DATA(val_short);
6072 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6077 // update my netplayer state if this is the final packet
6079 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6080 send_netplayer_update_packet();
6083 // the standalone server will receive this packet from the host of the game, to be applied locally and
6084 // also to be rebroadcast.
6085 if(Game_mode & GM_STANDALONE_SERVER){
6086 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6088 // add some mission sync text
6089 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6093 #define OBJ_VISIBILITY_DOT 0.6f
6095 // send and receive packets for shield explosion information
6096 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6099 ubyte data[MAX_PACKET_SIZE], utri_num;
6102 // Assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6104 Assert( tri_num < UCHAR_MAX );
6105 utri_num = (ubyte)tri_num;
6107 // for each player, determine if this object is behind the player -- if so, don't
6109 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6110 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6112 vector eye_to_obj_vec, diff, eye_pos;
6115 eye_pos = Net_players[i].s_info.eye_pos;
6116 eye_orient = Net_players[i].s_info.eye_orient;
6118 // check for same vectors
6119 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6120 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6124 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6125 dot = vm_vec_dot(&eye_orient.v.fvec, &eye_to_obj_vec);
6127 if ( dot < OBJ_VISIBILITY_DOT ){
6131 BUILD_HEADER(SHIELD_EXPLOSION);
6133 ADD_DATA( Objects[objnum].net_signature );
6136 multi_io_send(&Net_players[i], data, packet_size);
6141 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6143 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6150 // get the shield hit data
6151 offset = HEADER_LENGTH;
6152 GET_DATA(signature);
6154 //GET_DATA(hit_pos);
6157 // find the object with this signature. If found, then do a shield explosion
6158 objp = multi_get_network_object( signature );
6161 shield_info *shieldp;
6166 // given the tri num, find the local position which is the average of the
6167 // three vertices of the triangle affected. Use this average point as the hit
6169 // Assert( objp->type == OBJ_SHIP );
6170 if(objp->type != OBJ_SHIP){
6174 pm = model_get(Ships[objp->instance].modelnum);
6175 shieldp = &pm->shield;
6176 Assert( utri_num < shieldp->ntris );
6177 stri = shieldp->tris[utri_num];
6178 vm_vec_zero(&hit_pos);
6179 for ( i = 0; i < 3; i++ ) {
6180 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6182 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6183 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6187 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6190 ubyte data[MAX_PACKET_SIZE], val;
6192 int packet_size = 0;
6197 sc = &pl->player->stats;
6200 BUILD_HEADER(PLAYER_STATS);
6202 // add the player id
6203 ADD_DATA(pl->player_id);
6205 // add the byte indicating whether these stats are all-time or not
6206 val = (ubyte)stats_code;
6209 // kill information - alltime
6213 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6214 u_tmp = sc->kills[idx];
6217 // medal information
6218 for(idx=0;idx<NUM_MEDALS;idx++){
6219 i_tmp = sc->medals[idx];
6223 ADD_DATA(sc->score);
6225 ADD_DATA(sc->assists);
6226 ADD_DATA(sc->kill_count);
6227 ADD_DATA(sc->kill_count_ok);
6228 ADD_DATA(sc->p_shots_fired);
6229 ADD_DATA(sc->s_shots_fired);
6230 ADD_DATA(sc->p_shots_hit);
6231 ADD_DATA(sc->s_shots_hit);
6232 ADD_DATA(sc->p_bonehead_hits);
6233 ADD_DATA(sc->s_bonehead_hits);
6234 ADD_DATA(sc->bonehead_kills);
6236 ADD_DATA(sc->missions_flown);
6237 ADD_DATA(sc->flight_time);
6238 ADD_DATA(sc->last_flown);
6239 ADD_DATA(sc->last_backup);
6244 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6245 u_tmp = sc->m_okKills[idx];
6249 ADD_DATA(sc->m_score);
6250 ADD_DATA(sc->m_assists);
6251 ADD_DATA(sc->m_kill_count);
6252 ADD_DATA(sc->m_kill_count_ok);
6253 ADD_DATA(sc->mp_shots_fired);
6254 ADD_DATA(sc->ms_shots_fired);
6255 ADD_DATA(sc->mp_shots_hit);
6256 ADD_DATA(sc->ms_shots_hit);
6257 ADD_DATA(sc->mp_bonehead_hits);
6258 ADD_DATA(sc->ms_bonehead_hits);
6259 ADD_DATA(sc->m_bonehead_kills);
6260 ADD_DATA(sc->m_player_deaths);
6261 ADD_DATA(sc->m_medal_earned);
6264 case STATS_MISSION_KILLS:
6265 ADD_DATA(sc->m_kill_count);
6266 ADD_DATA(sc->m_kill_count_ok);
6267 ADD_DATA(sc->m_assists);
6270 case STATS_DOGFIGHT_KILLS:
6271 for(idx=0; idx<MAX_PLAYERS; idx++){
6272 u_tmp = sc->m_dogfight_kills[idx];
6275 ADD_DATA(sc->m_kill_count);
6276 ADD_DATA(sc->m_kill_count_ok);
6277 ADD_DATA(sc->m_assists);
6281 Assert(packet_size < MAX_PACKET_SIZE);
6283 // if we're a client, always send the data to the server
6284 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6285 multi_io_send_reliable(Net_player, data, packet_size);
6287 // otherwise do server specific stuff
6289 // send to a specific target
6291 multi_io_send_reliable(target, data, packet_size);
6293 // otherwise, send to everyone
6295 multi_io_send_to_all_reliable(data, packet_size);
6300 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6304 scoring_struct *sc,bogus;
6306 int offset = HEADER_LENGTH;
6310 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6312 // get the player who these stats are for
6313 GET_DATA(player_id);
6314 player_num = find_player_id(player_id);
6315 if (player_num == -1) {
6316 nprintf(("Network", "Couldn't find player for stats update!\n"));
6317 ml_string("Couldn't find player for stats update!\n");
6322 sc = &Net_players[player_num].player->stats;
6325 // get the stats code
6329 ml_string("Received STATS_ALLTIME\n");
6332 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6334 sc->kills[idx] = u_tmp;
6337 // read in the stats
6338 for (idx=0; idx<NUM_MEDALS; idx++) {
6340 sc->medals[idx] = i_tmp;
6343 GET_DATA(sc->score);
6345 GET_DATA(sc->assists);
6346 GET_DATA(sc->kill_count);
6347 GET_DATA(sc->kill_count_ok);
6348 GET_DATA(sc->p_shots_fired);
6349 GET_DATA(sc->s_shots_fired);
6350 GET_DATA(sc->p_shots_hit);
6351 GET_DATA(sc->s_shots_hit);
6352 GET_DATA(sc->p_bonehead_hits);
6353 GET_DATA(sc->s_bonehead_hits);
6354 GET_DATA(sc->bonehead_kills);
6356 GET_DATA(sc->missions_flown);
6357 GET_DATA(sc->flight_time);
6358 GET_DATA(sc->last_flown);
6359 GET_DATA(sc->last_backup);
6363 ml_string("Received STATS_MISSION\n");
6365 // kills - mission OK
6366 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6368 sc->m_okKills[idx] = u_tmp;
6371 GET_DATA(sc->m_score);
6372 GET_DATA(sc->m_assists);
6373 GET_DATA(sc->m_kill_count);
6374 GET_DATA(sc->m_kill_count_ok);
6375 GET_DATA(sc->mp_shots_fired);
6376 GET_DATA(sc->ms_shots_fired);
6377 GET_DATA(sc->mp_shots_hit);
6378 GET_DATA(sc->ms_shots_hit);
6379 GET_DATA(sc->mp_bonehead_hits);
6380 GET_DATA(sc->ms_bonehead_hits);
6381 GET_DATA(sc->m_bonehead_kills);
6382 GET_DATA(sc->m_player_deaths);
6383 GET_DATA(sc->m_medal_earned);
6386 case STATS_MISSION_KILLS:
6387 ml_string("Received STATS_MISSION_KILLS\n");
6389 GET_DATA(sc->m_kill_count);
6390 GET_DATA(sc->m_kill_count_ok);
6391 GET_DATA(sc->m_assists);
6394 case STATS_DOGFIGHT_KILLS:
6395 ml_string("Received STATS_DOGFIGHT_KILLS\n");
6396 if(player_num >= 0){
6397 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6399 for(idx=0; idx<MAX_PLAYERS; idx++){
6401 sc->m_dogfight_kills[idx] = u_tmp;
6402 if(player_num >= 0){
6403 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6406 GET_DATA(sc->m_kill_count);
6407 GET_DATA(sc->m_kill_count_ok);
6408 GET_DATA(sc->m_assists);
6413 // if I'm the server of the game, I should always rebroadcast these stats
6414 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6415 // make sure these are alltime stats
6416 Assert(val == STATS_ALLTIME);
6418 multi_broadcast_stats(STATS_ALLTIME);
6422 // called to create asteroid stuff
6423 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6426 ubyte data[MAX_PACKET_SIZE];
6427 ubyte packet_type, atype;
6431 if (relvec != NULL )
6434 BUILD_HEADER( ASTEROID_INFO );
6435 packet_type = ASTEROID_CREATE;
6437 Assert( asteroid_type < UCHAR_MAX );
6438 atype = (ubyte)asteroid_type;
6440 ADD_DATA( packet_type );
6441 ADD_DATA( parent_objp->net_signature );
6442 ADD_DATA( new_objp->net_signature );
6446 multi_io_send_to_all(data, packet_size);
6449 void send_asteroid_throw( object *objp )
6452 ubyte data[MAX_PACKET_SIZE], packet_type;
6454 BUILD_HEADER( ASTEROID_INFO );
6456 // this packet type is an asteroid throw
6457 packet_type = ASTEROID_THROW;
6458 ADD_DATA( packet_type );
6459 ADD_DATA( objp->net_signature );
6460 ADD_DATA( objp->pos );
6461 ADD_DATA( objp->phys_info.vel );
6463 multi_io_send_to_all(data, packet_size);
6466 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6469 ubyte data[MAX_PACKET_SIZE], packet_type;
6473 if ( hitpos != NULL )
6476 // build up an asteroid hit packet
6477 BUILD_HEADER( ASTEROID_INFO );
6478 packet_type = ASTEROID_HIT;
6479 ADD_DATA( packet_type );
6480 ADD_DATA( objp->net_signature );
6482 if(other_objp == NULL){
6483 ushort invalid_sig = 0xffff;
6484 ADD_DATA(invalid_sig);
6486 ADD_DATA( other_objp->net_signature );
6491 multi_io_send_to_all(data, packet_size);
6494 void process_asteroid_info( ubyte *data, header *hinfo )
6499 offset = HEADER_LENGTH;
6500 GET_DATA( packet_type );
6502 // based on the packet type, do something interesting with an asteroid!
6503 switch( packet_type ) {
6505 case ASTEROID_CREATE: {
6506 ushort psignature, signature;
6509 object *parent_objp;
6511 GET_DATA( psignature );
6512 GET_DATA( signature );
6516 // after getting the values, set the next network signature, and call the create sub function
6517 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6518 parent_objp = multi_get_network_object( psignature );
6519 if ( parent_objp ) {
6520 asteroid_sub_create( parent_objp, atype, &relvec );
6522 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6529 // asteroid throw packet -- asteroid has wrapped bounds
6530 case ASTEROID_THROW: {
6535 GET_DATA( signature );
6538 objp = multi_get_network_object( signature );
6540 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6544 objp->phys_info.vel = vel;
6545 objp->phys_info.desired_vel = vel;
6549 case ASTEROID_HIT: {
6550 ushort signature, osignature;
6551 object *objp, *other_objp;
6555 GET_DATA( signature );
6556 GET_DATA( osignature );
6560 objp = multi_get_network_object( signature );
6561 if(osignature == 0xffff){
6564 other_objp = multi_get_network_object( osignature );
6567 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6571 if ( IS_VEC_NULL(&hitpos) ){
6572 asteroid_hit( objp, other_objp, NULL, damage );
6574 asteroid_hit( objp, other_objp, &hitpos, damage );
6577 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6578 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6579 weapon_hit( other_objp, objp, &hitpos );
6592 void send_host_restr_packet(char *callsign,int code,int mode)
6594 ubyte data[MAX_PACKET_SIZE],val;
6595 int packet_size = 0;
6597 // build the header and add the opcode
6598 BUILD_HEADER(HOST_RESTR_QUERY);
6605 ADD_STRING(callsign);
6607 // if I'm the standalone server, I should be sending this to the game host
6608 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6609 multi_io_send_reliable(Netgame.host, data, packet_size);
6611 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6613 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6614 multi_io_send_reliable(Net_player, data, packet_size);
6618 void process_host_restr_packet(ubyte *data, header *hinfo)
6622 int offset = HEADER_LENGTH;
6624 // get the opcode and the callsign
6627 GET_STRING(callsign);
6630 // do code specific operations
6632 // query to the host from standalone
6634 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6636 // set the join mode
6637 Multi_join_restr_mode = mode;
6639 // set the timestamp
6640 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6642 // notify the host of the event
6643 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6644 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6647 // affirmative reply from the host to the standalone
6649 Assert(Game_mode & GM_STANDALONE_SERVER);
6651 // let the player join if the timestamp has not already elapsed on the server
6652 if(Multi_restr_query_timestamp != -1){
6653 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6659 Assert(Game_mode & GM_STANDALONE_SERVER);
6660 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6661 Multi_restr_query_timestamp = -1;
6666 void send_netgame_end_error_packet(int notify_code,int err_code)
6670 int packet_size = 0;
6672 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6673 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6675 // build the header and add the notification and error codes
6676 BUILD_HEADER(NETGAME_END_ERROR);
6677 code = (char)notify_code;
6679 code = (char)err_code;
6683 multi_io_send_to_all_reliable(data, packet_size);
6686 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6688 int offset = HEADER_LENGTH;
6689 char notify_code,error_code;
6691 // get the error and notification codes
6692 GET_DATA(notify_code);
6693 GET_DATA(error_code);
6697 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6700 // sends info that a countermeasure succeeded.
6701 void send_countermeasure_success_packet( int objnum )
6703 int pnum, packet_size;
6704 ubyte data[MAX_PACKET_SIZE];
6706 pnum = multi_find_player_by_object( &Objects[objnum] );
6708 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6712 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6713 multi_io_send(&Net_players[pnum], data, packet_size);
6716 // start the flashing of my hud gauge
6717 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6721 offset = HEADER_LENGTH;
6724 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6725 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6728 #define UPDATE_IS_PAUSED (1<<0)
6729 #define UPDATE_HULL_INFO (1<<1)
6731 void send_client_update_packet(net_player *pl)
6733 ubyte data[MAX_PACKET_SIZE],val;
6734 int packet_size = 0;
6737 BUILD_HEADER(CLIENT_UPDATE);
6741 // add the pause status
6742 if ( Multi_pause_status ) {
6743 val |= UPDATE_IS_PAUSED;
6744 } 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) ) {
6745 val |= UPDATE_HULL_INFO;
6746 Assert( Player_ship ); // I"d better have one of these!!!!
6751 // if paused, add the net address of the guy who paused
6752 if(val & UPDATE_IS_PAUSED){
6753 Assert(Multi_pause_pauser != NULL);
6754 ADD_DATA(Multi_pause_pauser->player_id);
6757 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6758 if ( val & UPDATE_HULL_INFO ) {
6760 ubyte percent, ns, threats;
6763 ship_subsys *subsysp;
6766 // get the object for the player
6767 Assert( pl->player->objnum != -1 );
6769 objp = &Objects[pl->player->objnum];
6771 Assert ( objp->type == OBJ_SHIP );
6772 shipp = &Ships[objp->instance];
6773 sip = &Ship_info[shipp->ship_info_index];
6775 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6776 // percentage value since that should be close enough
6777 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6781 percent = (ubyte)temp;
6783 ADD_DATA( percent );
6785 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6786 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6787 ADD_DATA( percent );
6790 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6791 // both machines. Added as percent since that number <= 100
6793 // also write out the number of subsystems. We do this because the client might not know
6794 // about the object he is getting data for. (i.e. he killed the object already).
6795 ns = (ubyte)sip->n_subsystems;
6798 // now the subsystems.
6799 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6800 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6801 ADD_DATA( percent );
6804 // compute the threats for this player. Only compute the threats if this player is actually
6805 // playing (i.e. he has a ship)
6806 hud_update_reticle( pl->player );
6807 threats = (ubyte)pl->player->threat_flags;
6808 ADD_DATA( threats );
6810 // add his energy level for guns
6811 ADD_DATA(shipp->weapon_energy);
6813 // add his secondary bank ammo
6814 ADD_DATA(shipp->weapons.num_secondary_banks);
6815 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6816 ADD_DATA(shipp->weapons.secondary_bank_ammo[i]);
6821 ADD_DATA(pl->sv_last_pl);
6823 // send the packet reliably to the player
6824 multi_io_send(pl, data, packet_size);
6827 void process_client_update_packet(ubyte *data, header *hinfo)
6832 int is_paused, have_hull_info;
6835 float weapon_energy;
6836 int offset = HEADER_LENGTH;
6838 // get the header byte containing useful information
6841 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6842 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6844 // if we are paused, get who paused
6847 player_index = find_player_id(pauser);
6848 if(player_index != -1){
6849 Multi_pause_pauser = &Net_players[player_index];
6851 Multi_pause_pauser = NULL;
6855 // if we have hull information, then read it in.
6856 if ( have_hull_info ) {
6860 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
6862 ship_subsys *subsysp;
6866 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6867 // percentage value since that should be close enough
6868 GET_DATA( hull_percent );
6870 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
6872 shield_percent[i] = ub_tmp;
6875 // get the data for the subsystems
6876 GET_DATA( n_subsystems );
6877 for ( i = 0; i < n_subsystems; i++ ){
6879 subsystem_percent[i] = ub_tmp;
6882 GET_DATA( threats );
6884 // add his energy level for guns
6885 GET_DATA(weapon_energy);
6887 // add his secondary bank ammo
6888 GET_DATA(ammo_count);
6889 for(i=0; i<ammo_count; i++){
6893 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
6894 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
6895 // UDP client update packets. Only read this info if I have a ship.
6896 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
6897 shipp = Player_ship;
6899 sip = &Ship_info[shipp->ship_info_index];
6901 val = hull_percent * sip->initial_hull_strength / 100.0f;
6902 objp->hull_strength = val;
6904 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6905 val = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
6906 objp->shields[i] = val;
6909 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
6910 // then don't read these in.
6911 if ( n_subsystems == sip->n_subsystems ) {
6913 n_subsystems = 0; // reuse this variable
6914 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6917 val = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
6918 subsysp->current_hits = val;
6920 // add the value just generated (it was zero'ed above) into the array of generic system types
6921 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
6922 Assert ( subsys_type < SUBSYSTEM_MAX );
6923 shipp->subsys_info[subsys_type].current_hits += val;
6927 ship_recalc_subsys_strength( shipp );
6929 shipp->weapon_energy = weapon_energy;
6930 for(i=0; i<ammo_count; i++){
6931 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
6934 // update my threat flags.
6935 // temporarily commented out until tested.
6936 Net_player->player->threat_flags = threats;
6943 if(Net_player != NULL){
6944 Net_player->cl_last_pl = pl;
6948 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
6949 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
6951 multi_pause_pause();
6953 multi_pause_unpause();
6958 void send_countdown_packet(int time)
6962 int packet_size = 0;
6964 // build the header and add the time
6965 BUILD_HEADER(COUNTDOWN);
6969 // if we're the server, we should broadcast to everyone
6970 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6971 multi_io_send_to_all_reliable(data, packet_size);
6973 // otherwise we'de better be a host sending to the standalone
6975 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6976 multi_io_send_reliable(Net_player, data, packet_size);
6980 void process_countdown_packet(ubyte *data, header *hinfo)
6982 int offset = HEADER_LENGTH;
6989 // if we're not in the post sync data screen, ignore it
6990 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
6994 // if we're the standalone, this should be a -1 telling us to start the countdown
6995 if(Game_mode & GM_STANDALONE_SERVER){
6996 Assert((int)time == -1);
6998 // start the countdown
6999 multi_sync_start_countdown();
7001 // otherwise if we're clients, just bash the countdown
7003 Multi_sync_countdown = (int)time;
7007 // packets for debriefing information
7008 void send_debrief_info( int stage_count[], int *stages[] )
7010 ubyte data[MAX_PACKET_SIZE];
7011 int packet_size, i, j;
7014 BUILD_HEADER(DEBRIEF_INFO);
7016 // add the data for the teams
7017 for ( i = 0; i < Num_teams; i++ ) {
7020 count = stage_count[i];
7022 for ( j = 0; j < count; j++ ) {
7023 i_tmp = stages[i][j];
7028 multi_io_send_to_all_reliable(data, packet_size);
7031 // process a debrief info packet from the server
7032 void process_debrief_info( ubyte *data, header *hinfo )
7035 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7038 offset = HEADER_LENGTH;
7039 for ( i = 0; i < Num_teams; i++ ) {
7043 stage_counts[i] = count;
7044 stages[i] = active_stages[i];
7045 for ( j = 0; j < count; j++ ) {
7047 active_stages[i][j] = i_tmp;
7052 // now that we have the stage data for the debriefing stages, call debrief function with the
7053 // data so that clients can now see the debriefing stuff. Do it only for my team.
7054 Assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7055 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7058 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7059 // sends homing_object and homing_subsystem to all clients.
7060 void send_homing_weapon_info( int weapon_num )
7062 ubyte data[MAX_PACKET_SIZE];
7065 object *homing_object;
7066 ushort homing_signature;
7069 wp = &Weapons[weapon_num];
7071 // be sure that this weapon object is a homing object.
7072 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7075 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7076 // homing signature.
7077 homing_signature = 0;
7078 homing_object = wp->homing_object;
7079 if ( homing_object != NULL ) {
7080 homing_signature = homing_object->net_signature;
7082 // get the subsystem index.
7084 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7087 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7088 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
7089 t_subsys = (char)s_index;
7093 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7094 ADD_DATA( Objects[wp->objnum].net_signature );
7095 ADD_DATA( homing_signature );
7096 ADD_DATA( t_subsys );
7098 multi_io_send_to_all(data, packet_size);
7101 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7102 // packet contains information for multiple weapons (like hornets).
7103 void process_homing_weapon_info( ubyte *data, header *hinfo )
7106 ushort weapon_signature, homing_signature;
7108 object *homing_object, *weapon_objp;
7111 offset = HEADER_LENGTH;
7113 // get the data for the packet
7114 GET_DATA( weapon_signature );
7115 GET_DATA( homing_signature );
7116 GET_DATA( h_subsys );
7119 // deal with changing this weapons homing information
7120 weapon_objp = multi_get_network_object( weapon_signature );
7121 if ( weapon_objp == NULL ) {
7122 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7125 Assert( weapon_objp->type == OBJ_WEAPON );
7126 wp = &Weapons[weapon_objp->instance];
7128 // be sure that we can find these weapons and
7129 homing_object = multi_get_network_object( homing_signature );
7130 if ( homing_object == NULL ) {
7131 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7135 if ( homing_object->type == OBJ_WEAPON ) {
7136 Assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7139 wp->homing_object = homing_object;
7140 wp->homing_subsys = NULL;
7141 wp->target_num = OBJ_INDEX(homing_object);
7142 wp->target_sig = homing_object->signature;
7143 if ( h_subsys != -1 ) {
7144 Assert( homing_object->type == OBJ_SHIP );
7145 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7148 if ( homing_object->type == OBJ_SHIP ) {
7149 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7153 void send_emp_effect(ushort net_sig, float intensity, float time)
7158 Assert(MULTIPLAYER_MASTER);
7160 // build the packet and add the opcode
7161 BUILD_HEADER(EMP_EFFECT);
7163 ADD_DATA(intensity);
7166 // send it to the player
7167 multi_io_send_to_all(data, packet_size);
7170 void process_emp_effect(ubyte *data, header *hinfo)
7172 float intensity, time;
7175 int offset = HEADER_LENGTH;
7177 // read in the EMP effect data
7179 GET_DATA(intensity);
7183 // try and find the object
7184 objp = multi_get_network_object(net_sig);
7185 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7186 // if i'm not an observer and I have a valid ship, play the EMP effect
7187 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7188 emp_start_local(intensity, time);
7191 // start the effect for the ship itself
7192 emp_start_ship(objp, intensity, time);
7196 // tells whether or not reinforcements are available
7197 void send_reinforcement_avail( int rnum )
7202 BUILD_HEADER(REINFORCEMENT_AVAIL);
7204 multi_io_send_to_all_reliable(data, packet_size);
7207 void process_reinforcement_avail( ubyte *data, header *hinfo )
7212 offset = HEADER_LENGTH;
7216 // sanity check for a valid reinforcement number
7217 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7218 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7222 void send_change_iff_packet(ushort net_signature, int new_team)
7224 ubyte data[MAX_PACKET_SIZE];
7225 int packet_size = 0;
7227 if(Net_player == NULL){
7230 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7234 // build the packet and add the data
7235 BUILD_HEADER(CHANGE_IFF);
7236 ADD_DATA(net_signature);
7239 // send to all players
7240 multi_io_send_to_all_reliable(data, packet_size);
7243 void process_change_iff_packet( ubyte *data, header *hinfo)
7245 int offset = HEADER_LENGTH;
7246 ushort net_signature;
7251 GET_DATA(net_signature);
7255 // lookup the object
7256 objp = multi_get_network_object(net_signature);
7257 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7258 Ships[objp->instance].team = new_team;
7262 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7264 int packet_size, objnum;
7265 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7268 net_player *ignore = NULL;
7270 // sanity checking for now
7271 Assert ( banks_fired <= 3 );
7273 // get an object pointer for this ship.
7274 objnum = shipp->objnum;
7275 objp = &Objects[objnum];
7277 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7278 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7282 // just in case nothing got fired
7283 if(banks_fired <= 0){
7287 // ubanks_fired = (ubyte)banks_fired;
7288 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7289 // Assert( current_bank <= 3 );
7291 // insert the current primary bank into this byte
7292 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7294 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7295 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7296 // ubanks_fired |= (1<<7);
7299 // determine if its a player ship and don't send to him if we're in "client firing" mode
7300 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7301 if(MULTIPLAYER_MASTER){
7302 np_index = multi_find_player_by_net_signature(objp->net_signature);
7303 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7304 ignore = &Net_players[np_index];
7308 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7309 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7310 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7311 // and server in sync w.r.t. weapon energy for player ship
7312 BUILD_HEADER( PRIMARY_FIRED_NEW );
7313 ADD_DATA(objp->net_signature);
7314 // ADD_DATA(ubanks_fired);
7316 // if I'm a server, broadcast to all players
7317 if(MULTIPLAYER_MASTER){
7318 multi_io_send_to_all(data, packet_size, ignore);
7321 multi_rate_add(1, "wfi", packet_size);
7323 // otherwise just send to the server
7325 multi_io_send(Net_player, data, packet_size);
7329 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7331 int offset; // linked;
7332 // ubyte banks_fired, current_bank;
7337 // read all packet info
7338 offset = HEADER_LENGTH;
7339 GET_DATA(shooter_sig);
7340 // GET_DATA(banks_fired);
7343 // find the object this fired packet is operating on
7344 objp = multi_get_network_object( shooter_sig );
7345 if ( objp == NULL ) {
7346 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7349 // if this object is not actually a valid ship, don't do anything
7350 if(objp->type != OBJ_SHIP){
7353 if(objp->instance < 0){
7356 shipp = &Ships[objp->instance];
7358 // get the link status of the primary banks
7360 // if ( banks_fired & (1<<7) ) {
7362 // banks_fired ^= (1<<7);
7365 // get the current primary bank
7366 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7367 // current_bank &= 0x3;
7368 // Assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7369 // shipp->weapons.current_primary_bank = current_bank;
7371 // strip off all remaining bits and just keep which banks were actually fired.
7372 // banks_fired &= 0x3;
7374 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7377 // shipp->flags &= ~SF_PRIMARY_LINKED;
7379 // shipp->flags |= SF_PRIMARY_LINKED;
7382 // if we're in client firing mode, ignore ones for myself
7383 if((Player_obj != NULL) && (Player_obj == objp)){
7387 ship_fire_primary( objp, 0, 1 );
7390 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7392 ubyte data[MAX_PACKET_SIZE];
7395 net_player *ignore = NULL;
7397 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7398 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7402 Assert ( cmeasure_count < UCHAR_MAX );
7403 BUILD_HEADER(COUNTERMEASURE_NEW);
7404 ADD_DATA( objp->net_signature );
7405 ADD_DATA( rand_val );
7407 nprintf(("Network","Sending NEW countermeasure packet!\n"));
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 // if I'm the server, send to all players
7419 if(MULTIPLAYER_MASTER){
7420 multi_io_send_to_all(data, packet_size, ignore);
7422 // otherwise send to the server
7424 multi_io_send(Net_player, data, packet_size);
7428 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7435 offset = HEADER_LENGTH;
7436 GET_DATA( signature );
7437 GET_DATA( rand_val );
7440 objp = multi_get_network_object( signature );
7441 if ( objp == NULL ) {
7442 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7445 if(objp->type != OBJ_SHIP){
7449 // if we're in client firing mode, ignore ones for myself
7450 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7451 if((Player_obj != NULL) && (Player_obj == objp)){
7455 // make it so ship can fire right away!
7456 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7457 if ( objp == Player_obj ){
7458 nprintf(("network", "firing countermeasure from my ship\n"));
7460 ship_launch_countermeasure( objp, rand_val );
7463 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7465 ubyte data[MAX_PACKET_SIZE];
7466 int packet_size = 0;
7470 // only the server should ever be doing this
7471 Assert(MULTIPLAYER_MASTER);
7473 // setup outgoing data
7474 Assert(shooter != NULL);
7475 Assert(turret != NULL);
7476 Assert(target != NULL);
7477 Assert(override != NULL);
7478 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7481 u_beam_info = (ubyte)beam_info_index;
7482 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7483 Assert(subsys_index >= 0);
7484 if(subsys_index < 0){
7489 BUILD_HEADER(BEAM_FIRED);
7490 ADD_DATA(shooter->net_signature);
7491 ADD_DATA(subsys_index);
7492 ADD_DATA(target->net_signature);
7493 ADD_DATA(u_beam_info);
7494 ADD_DATA((*override));
7496 // send to all clients
7497 multi_io_send_to_all_reliable(data, packet_size);
7500 void process_beam_fired_packet(ubyte *data, header *hinfo)
7503 ushort shooter_sig, target_sig;
7507 beam_fire_info fire_info;
7509 // only clients should ever get this
7510 Assert(MULTIPLAYER_CLIENT);
7512 // read in packet data
7513 offset = HEADER_LENGTH;
7514 GET_DATA(shooter_sig);
7515 GET_DATA(subsys_index);
7516 GET_DATA(target_sig);
7517 GET_DATA(u_beam_info);
7521 // lookup all relevant data
7522 fire_info.beam_info_index = (int)u_beam_info;
7523 fire_info.shooter = NULL;
7524 fire_info.target = NULL;
7525 fire_info.turret = NULL;
7526 fire_info.target_subsys = NULL;
7527 fire_info.beam_info_override = NULL;
7528 fire_info.shooter = multi_get_network_object(shooter_sig);
7529 fire_info.target = multi_get_network_object(target_sig);
7530 fire_info.beam_info_override = &b_info;
7531 fire_info.accuracy = 1.0f;
7532 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)){
7533 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7536 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7537 if(fire_info.turret == NULL){
7538 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7543 beam_fire(&fire_info);
7546 void send_sw_query_packet(ubyte code, char *txt)
7548 ubyte data[MAX_PACKET_SIZE];
7549 int packet_size = 0;
7551 // build the packet and add the code
7552 BUILD_HEADER(SW_STD_QUERY);
7554 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7558 // if I'm the host, send to standalone
7559 if(MULTIPLAYER_HOST){
7560 Assert(!MULTIPLAYER_MASTER);
7561 Assert(code == SW_STD_START);
7562 multi_io_send_reliable(Net_player, data, packet_size);
7564 // otherwise standalone sends back to host
7566 Assert(Game_mode & GM_STANDALONE_SERVER);
7567 Assert(code != SW_STD_START);
7568 Assert(Netgame.host != NULL);
7569 if(Netgame.host != NULL){
7570 multi_io_send_reliable(Netgame.host, data, packet_size);
7575 void process_sw_query_packet(ubyte *data, header *hinfo)
7579 void send_event_update_packet(int event)
7581 ubyte data[MAX_PACKET_SIZE];
7582 ushort u_event = (ushort)event;
7583 int packet_size = 0;
7585 // build the header and add the event
7586 BUILD_HEADER(EVENT_UPDATE);
7588 ADD_DATA(Mission_events[event].flags);
7589 ADD_DATA(Mission_events[event].formula);
7590 ADD_DATA(Mission_events[event].result);
7591 ADD_DATA(Mission_events[event].count);
7593 // send to all players
7594 multi_io_send_to_all_reliable(data, packet_size);
7597 void process_event_update_packet(ubyte *data, header *hinfo)
7599 int offset = HEADER_LENGTH;
7605 store_flags = Mission_events[u_event].flags;
7606 GET_DATA(Mission_events[u_event].flags);
7607 GET_DATA(Mission_events[u_event].formula);
7608 GET_DATA(Mission_events[u_event].result);
7609 GET_DATA(Mission_events[u_event].count);
7612 // went from non directive special to directive special
7613 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7614 mission_event_set_directive_special(u_event);
7616 // if we went directive special to non directive special
7617 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7618 mission_event_unset_directive_special(u_event);
7622 // weapon detonate packet
7623 void send_weapon_detonate_packet(object *objp)
7625 ubyte data[MAX_PACKET_SIZE];
7626 int packet_size = 0;
7629 Assert(MULTIPLAYER_MASTER);
7630 if(!MULTIPLAYER_MASTER){
7633 Assert(objp != NULL);
7638 // build the header and add the data
7639 BUILD_HEADER(WEAPON_DET);
7640 ADD_DATA(objp->net_signature);
7642 // send to all players
7643 multi_io_send_to_all(data, packet_size);
7646 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7649 int offset = HEADER_LENGTH;
7650 object *objp = NULL;
7652 // get the weapon signature
7656 // lookup the weapon
7657 objp = multi_get_network_object(net_sig);
7658 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7659 weapon_detonate(objp);
7663 // flak fired packet
7664 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7667 ushort pnet_signature;
7668 ubyte data[MAX_PACKET_SIZE], cindex;
7674 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7678 // local setup -- be sure we are actually passing a weapon!!!!
7679 objp = &Objects[weapon_objnum];
7680 Assert ( objp->type == OBJ_WEAPON );
7681 pnet_signature = Objects[ship_objnum].net_signature;
7683 Assert( subsys_index < UCHAR_MAX );
7684 cindex = (ubyte)subsys_index;
7686 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7691 // build the fire turret packet.
7692 BUILD_HEADER(FLAK_FIRED);
7693 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
7694 ADD_DATA( pnet_signature );
7696 val = (short)ssp->submodel_info_1.angs.h;
7698 val = (short)ssp->submodel_info_2.angs.p;
7700 ADD_DATA( flak_range );
7702 multi_io_send_to_all(data, packet_size);
7704 multi_rate_add(1, "flk", packet_size);
7707 void process_flak_fired_packet(ubyte *data, header *hinfo)
7709 int offset, weapon_objnum, wid;
7710 ushort pnet_signature;
7718 short pitch, heading;
7721 // get the data for the turret fired packet
7722 offset = HEADER_LENGTH;
7723 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7724 GET_DATA( pnet_signature );
7725 GET_DATA( turret_index );
7726 GET_DATA( heading );
7728 GET_DATA( flak_range );
7729 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7732 objp = multi_get_network_object( pnet_signature );
7733 if ( objp == NULL ) {
7734 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7738 // if this isn't a ship, do nothing
7739 if ( objp->type != OBJ_SHIP ){
7743 // make an orientation matrix from the o_fvec
7744 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7746 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7747 // hack, but should be suitable.
7748 shipp = &Ships[objp->instance];
7749 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7753 wid = ssp->system_info->turret_weapon_type;
7754 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7758 // bash the position and orientation of the turret
7759 ssp->submodel_info_1.angs.h = (float)heading;
7760 ssp->submodel_info_2.angs.p = (float)pitch;
7762 // get the world position of the weapon
7763 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7765 // create the weapon object
7766 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7767 if (weapon_objnum != -1) {
7768 if ( Weapon_info[wid].launch_snd != -1 ) {
7769 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7772 // create a muzzle flash from a flak gun based upon firing position and weapon type
7773 flak_muzzle_flash(&pos, &dir, wid);
7775 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7776 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7780 #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);
7781 #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);
7783 // player pain packet
7784 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7786 ubyte data[MAX_PACKET_SIZE];
7789 int packet_size = 0;
7791 Assert(MULTIPLAYER_MASTER);
7792 if(!MULTIPLAYER_MASTER){
7800 // build the packet and add the code
7801 BUILD_HEADER(NETPLAYER_PAIN);
7802 windex = (ubyte)weapon_info_index;
7804 udamage = (ushort)damage;
7807 ADD_DATA((*hitpos));
7809 // send to the player
7810 multi_io_send(pl, data, packet_size);
7812 multi_rate_add(1, "pai", packet_size);
7815 void process_player_pain_packet(ubyte *data, header *hinfo)
7821 vector local_hit_pos;
7824 // get the data for the pain packet
7825 offset = HEADER_LENGTH;
7829 GET_DATA(local_hit_pos);
7832 mprintf(("PAIN!\n"));
7834 // get weapon info pointer
7835 Assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER));
7836 if(! ((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
7839 wip = &Weapon_info[windex];
7841 // play the weapon hit sound
7842 Assert(Player_obj != NULL);
7843 if(Player_obj == NULL){
7846 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
7848 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
7849 ship_hit_pain((float)udamage);
7852 ship_apply_whack(&force, &local_hit_pos, Player_obj);
7856 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
7858 ubyte data[MAX_PACKET_SIZE];
7860 int packet_size = 0;
7862 // build the header and add the data
7863 BUILD_HEADER(LIGHTNING_PACKET);
7864 val = (char)bolt_type;
7867 ADD_DATA((*strike));
7869 // send to everyone unreliable for now
7870 multi_io_send_to_all(data, packet_size);
7873 void process_lightning_packet(ubyte *data, header *hinfo)
7877 vector start, strike;
7880 offset = HEADER_LENGTH;
7881 GET_DATA(bolt_type);
7892 nebl_bolt(bolt_type, &start, &strike);
7895 void send_bytes_recvd_packet(net_player *pl)
7897 // only clients should ever be doing this
7902 ubyte data[MAX_PACKET_SIZE];
7903 int packet_size = 0;
7904 BUILD_HEADER(BYTES_SENT);
7905 ADD_DATA(pl->cl_bytes_recvd);
7907 // send to the server
7908 multi_io_send_reliable(pl, data, packet_size);
7911 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
7915 net_player *pl = NULL;
7916 int offset = HEADER_LENGTH;
7922 if(Net_player == NULL){
7925 if(!MULTIPLAYER_MASTER){
7929 // make sure we know what player sent this
7930 pid = find_player_id(hinfo->id);
7931 if((pid < 0) || (pid >= MAX_PLAYERS)){
7934 pl = &Net_players[pid];
7937 pl->cl_bytes_recvd = bytes;
7941 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
7944 pl->sv_bytes_sent = 0;
7948 void send_host_captain_change_packet(short player_id, int captain_change)
7950 ubyte data[MAX_PACKET_SIZE];
7951 int packet_size = 0;
7954 BUILD_HEADER(TRANSFER_HOST);
7955 ADD_DATA(player_id);
7956 ADD_DATA(captain_change);
7959 multi_io_send_to_all_reliable(data, packet_size);
7962 void process_host_captain_change_packet(ubyte *data, header *hinfo)
7964 int offset = HEADER_LENGTH;
7965 int idx, found_player, captain_change;
7968 // get the player id
7969 GET_DATA(player_id);
7970 GET_DATA(captain_change);
7976 for(idx=0; idx<MAX_PLAYERS; idx++){
7977 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
7978 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
7983 // unflag all old players
7984 for(idx=0; idx<MAX_PLAYERS; idx++){
7985 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
7990 for(idx=0; idx<MAX_PLAYERS; idx++){
7991 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
7992 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
7994 // spew to the HUD config
7995 if(Net_players[idx].player != NULL){
7996 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
8006 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
8011 void send_self_destruct_packet()
8013 ubyte data[MAX_PACKET_SIZE];
8014 int packet_size = 0;
8017 if(Net_player == NULL){
8021 // if i'm the server, I shouldn't be here
8022 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8023 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8027 // only if this is valid
8028 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8033 if((Player_ship == NULL) || (Player_obj == NULL)){
8038 BUILD_HEADER(SELF_DESTRUCT);
8039 ADD_DATA(Player_obj->net_signature);
8041 // send to the server
8042 multi_io_send_reliable(Net_player, data, packet_size);
8045 void process_self_destruct_packet(ubyte *data, header *hinfo)
8047 int offset = HEADER_LENGTH;
8051 // get the net signature
8056 np_index = find_player_id(hinfo->id);
8060 if(MULTI_OBSERVER(Net_players[np_index])){
8063 if(Net_players[np_index].player == NULL){
8066 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8069 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8072 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8075 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8080 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);