2 * $Logfile: /Freespace2/code/Network/MultiMsgs.cpp $
7 * C file that holds functions for the building and processing of multiplayer packets
10 * Revision 1.3 2002/05/26 20:49:54 theoddone33
13 * Revision 1.2 2002/05/07 03:16:47 theoddone33
14 * The Great Newline Fix
16 * Revision 1.1.1.1 2002/05/03 03:28:10 root
20 * 83 9/14/99 2:21p Dave
21 * Fixed observer mode joining and ingame stuff.
23 * 82 9/14/99 3:26a Dave
24 * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
25 * respawn-too-early problem. Made a few crash points safe.
27 * 81 9/13/99 4:52p Dave
30 * 80 9/08/99 10:01p Dave
31 * Make sure game won't run in a drive's root directory. Make sure
32 * standalone routes suqad war messages properly to the host.
34 * 79 8/28/99 4:54p Dave
35 * Fixed directives display for multiplayer clients for wings with
36 * multiple waves. Fixed hud threat indicator rendering color.
38 * 78 8/27/99 12:32a Dave
39 * Allow the user to specify a local port through the launcher.
41 * 77 8/26/99 8:51p Dave
42 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
44 * 76 8/25/99 4:38p Dave
45 * Updated PXO stuff. Make squad war report stuff much more nicely.
47 * 75 8/24/99 1:50a Dave
48 * Fixed client-side afterburner stuttering. Added checkbox for no version
49 * checking on PXO join. Made button info passing more friendly between
52 * 74 8/22/99 5:53p Dave
53 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
54 * instead of ship designations for multiplayer players.
56 * 73 8/22/99 1:55p Dave
57 * Cleaned up host/team-captain leaving code.
59 * 72 8/22/99 1:19p Dave
60 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
61 * which d3d cards are detected.
63 * 71 8/19/99 10:59a Dave
64 * Packet loss detection.
66 * 70 8/17/99 1:12p Dave
67 * Send TvT update when a client has finished joining so he stays nice and
70 * 69 8/16/99 4:05p Dave
71 * Big honking checkin.
73 * 68 8/11/99 5:54p Dave
74 * Fixed collision problem. Fixed standalone ghost problem.
76 * 67 8/06/99 9:46p Dave
77 * Hopefully final changes for the demo.
79 * 66 8/05/99 2:06a Dave
82 * 65 7/30/99 7:01p Dave
83 * Dogfight escort gauge. Fixed up laser rendering in Glide.
85 * 64 7/29/99 5:41p Jefff
86 * Sound hooks for cmeasure success
88 * 63 7/28/99 5:34p Dave
89 * Nailed the missing stats bug to the wall. Problem was optimized build
90 * and using GET_DATA() with array elements. BLECH.
92 * 62 7/26/99 5:50p Dave
93 * Revised ingame join. Better? We'll see....
95 * 61 7/24/99 1:54p Dave
96 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
99 * 60 7/22/99 7:17p Dave
100 * Fixed excessive whacks in multiplayer.
102 * 59 7/08/99 10:53a Dave
103 * New multiplayer interpolation scheme. Not 100% done yet, but still
104 * better than the old way.
106 * 58 7/03/99 5:50p Dave
107 * Make rotated bitmaps draw properly in padlock views.
109 * 57 7/03/99 4:08p Dave
110 * Fixed wss_slots size issues. Fixed potentially nasty bug in low level
113 * 56 6/21/99 7:24p Dave
114 * netplayer pain packet. Added type E unmoving beams.
116 * 55 6/18/99 5:16p Dave
117 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
118 * dialog to PXO screen.
120 * 54 6/16/99 4:06p Dave
121 * New pilot info popup. Added new draw-bitmap-as-poly function.
123 * 53 6/16/99 10:20a Dave
124 * Added send-message-list sexpression.
126 * 52 6/04/99 3:52p Anoop
127 * Removed bogus assert.
129 * 51 6/01/99 8:35p Dave
130 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
131 * awacs-set-radius sexpression.
133 * 50 5/21/99 5:03p Andsager
134 * Add code to display engine wash death. Modify ship_kill_packet
136 * 49 5/18/99 1:30p Dave
137 * Added muzzle flash table stuff.
139 * 48 5/14/99 1:59p Andsager
140 * Multiplayer message for subsystem cargo revealed.
142 * 47 5/14/99 12:15p Andsager
143 * Add vaporized to kill packet
145 * 46 5/03/99 8:32p Dave
146 * New version of multi host options screen.
148 * 45 4/30/99 12:18p Dave
149 * Several minor bug fixes.
151 * 44 4/29/99 2:29p Dave
152 * Made flak work much better in multiplayer.
154 * 43 4/28/99 11:13p Dave
155 * Temporary checkin of artillery code.
157 * 42 4/16/99 5:54p Dave
158 * Support for on/off style "stream" weapons. Real early support for
159 * target-painting lasers.
161 * 41 4/12/99 2:22p Dave
162 * More checks for dogfight stats.
164 * 40 4/09/99 2:21p Dave
165 * Multiplayer beta stuff. CD checking.
167 * 39 4/02/99 9:55a Dave
168 * Added a few more options in the weapons.tbl for beam weapons. Attempt
169 * at putting "pain" packets into multiplayer.
171 * 38 4/01/99 3:41p Anoop
172 * Removed bogus Int3().
174 * 37 3/19/99 9:51a Dave
175 * Checkin to repair massive source safe crash. Also added support for
176 * pof-style nebulae, and some new weapons code.
178 * 38 3/12/99 2:32p Anoop
179 * Removed bogus asserts.
181 * 37 3/11/99 11:41a Neilk
182 * Don't do multi_io_* operations in single-player
184 * 36 3/10/99 6:50p Dave
185 * Changed the way we buffer packets for all clients. Optimized turret
186 * fired packets. Did some weapon firing optimizations.
188 * 35 3/09/99 6:24p Dave
189 * More work on object update revamping. Identified several sources of
190 * unnecessary bandwidth.
192 * 34 3/08/99 7:03p Dave
193 * First run of new object update system. Looks very promising.
195 * 33 3/04/99 6:09p Dave
196 * Added in sexpressions for firing beams and checking for if a ship is
199 * 32 3/01/99 10:00a Dave
200 * Fxied several dogfight related stats bugs.
202 * 31 2/24/99 2:25p Dave
203 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
204 * bug for dogfight more.
206 * 30 2/23/99 2:29p Dave
207 * First run of oldschool dogfight mode.
209 * 29 2/21/99 6:01p Dave
210 * Fixed standalone WSS packets.
212 * 28 2/21/99 1:48p Dave
213 * Some code for monitoring datarate for multiplayer in detail.
215 * 27 2/17/99 2:11p Dave
216 * First full run of squad war. All freespace and tracker side stuff
219 * 26 2/12/99 6:16p Dave
220 * Pre-mission Squad War code is 95% done.
222 * 25 2/11/99 3:08p Dave
223 * PXO refresh button. Very preliminary squad war support.
225 * 24 1/29/99 5:07p Dave
226 * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
229 * 23 1/27/99 9:56a Dave
230 * Temporary checkin of beam weapons for Dan to make cool sounds.
232 * 22 1/26/99 6:33p Anoop
233 * Fixed multiplayer slot switching problem (be sure to remember that
234 * hinfo->id is player id# _not_ player index #)
236 * 21 1/24/99 11:37p Dave
237 * First full rev of beam weapons. Very customizable. Removed some bogus
238 * Int3()'s in low level net code.
240 * 20 1/15/99 4:37p Dave
241 * Potential fix for weapon pair problem.
243 * 19 1/14/99 6:06p Dave
244 * 100% full squad logo support for single player and multiplayer.
246 * 18 1/14/99 12:48a Dave
247 * Todo list bug fixes. Made a pass at putting briefing icons back into
248 * FRED. Sort of works :(
250 * 17 1/12/99 5:45p Dave
251 * Moved weapon pipeline in multiplayer to almost exclusively client side.
252 * Very good results. Bandwidth goes down, playability goes up for crappy
253 * connections. Fixed object update problem for ship subsystems.
255 * 16 1/08/99 4:56p Anoop
256 * Fixed a problem with wss request packets.
258 * 15 12/18/98 12:24p Markm
259 * Fixed a dumb bug where player image_filenames were not being passed
260 * properly in new players packet.
262 * 14 12/14/98 12:13p Dave
263 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
266 * 13 11/30/98 1:07p Dave
267 * 16 bit conversion, first run.
269 * 12 11/20/98 4:08p Dave
270 * Fixed flak effect in multiplayer.
272 * 11 11/19/98 4:19p Dave
273 * Put IPX sockets back in psnet. Consolidated all multiplayer config
276 * 10 11/19/98 8:04a Dave
277 * Full support for D3-style reliable sockets. Revamped packet lag/loss
278 * system, made it receiver side and at the lowest possible level.
280 * 9 11/17/98 11:12a Dave
281 * Removed player identification by address. Now assign explicit id #'s.
283 * 8 11/12/98 11:50a Dave
284 * Multiplayer clients set flak range to be very long.
286 * 7 11/12/98 12:13a Dave
287 * Tidied code up for multiplayer test. Put in network support for flak
290 * 6 11/05/98 5:55p Dave
291 * Big pass at reducing #includes
293 * 5 10/20/98 1:39p Andsager
294 * Make so sparks follow animated ship submodels. Modify
295 * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
296 * submodel_num. Add submodel_num to multiplayer hit packet.
298 * 4 10/13/98 9:29a Dave
299 * Started neatening up freespace.h. Many variables renamed and
300 * reorganized. Added AlphaColors.[h,cpp]
302 * 3 10/07/98 6:27p Dave
303 * Globalized mission and campaign file extensions. Removed Silent Threat
304 * special code. Moved \cache \players and \multidata into the \data
307 * 2 10/07/98 10:53a Dave
310 * 1 10/07/98 10:50a Dave
312 * 506 10/02/98 3:22p Allender
313 * fix up the -connect option and fix the -port option
315 * 505 10/02/98 11:45a Dave
316 * Fixed stupid chat message bug.
318 * 504 9/29/98 1:33p Dave
319 * Remove standalone only conditional compiles for pre 1.04 stuff.
321 * 503 9/28/98 1:54p Dave
322 * Make sure French and German don't xfer builtin files they don't have
325 * 502 9/20/98 7:19p Dave
326 * Added CHANGE_IFF packet.
328 * 501 9/17/98 3:08p Dave
329 * PXO to non-pxo game warning popup. Player icon stuff in create and join
330 * game screens. Upped server count refresh time in PXO to 35 secs (from
339 #include <io.h> // for findfirst/findnext, etc
342 #include "multimsgs.h"
343 #include "multiutil.h"
346 #include "multiteamselect.h"
347 #include "linklist.h"
348 #include "gamesequence.h"
349 #include "hudmessage.h"
350 #include "hudsquadmsg.h"
351 #include "freespace.h"
355 #include "missiongoals.h"
356 #include "missionparse.h"
357 #include "missionlog.h"
358 #include "missionmessage.h"
359 #include "missionbrief.h"
361 #include "cmeasure.h"
362 #include "model.h" // for some limits
363 #include "afterburner.h"
364 #include "stand_gui.h"
365 #include "multi_xfer.h"
371 #include "managepilot.h"
372 #include "hudsquadmsg.h"
374 #include "missionweaponchoice.h"
375 #include "missionshipchoice.h"
376 #include "fireballs.h"
379 #include "multi_ingame.h"
380 #include "multiteamselect.h"
382 #include "multi_campaign.h"
383 #include "multi_team.h"
384 #include "multi_respawn.h"
385 #include "multi_observer.h"
386 #include "multi_voice.h"
387 #include "asteroid.h"
388 #include "multi_pmsg.h"
389 #include "multi_data.h"
390 #include "multi_options.h"
391 #include "objcollide.h"
392 #include "hudreticle.h"
393 #include "multi_pause.h"
394 #include "multi_endgame.h"
395 #include "missiondebrief.h"
396 #include "multi_obj.h"
397 #include "multi_log.h"
399 #include "multi_kick.h"
403 #include "multi_rate.h"
404 #include "neblightning.h"
405 #include "hudescort.h"
407 // #define _MULTI_SUPER_WACKY_COMPRESSION
409 #ifdef _MULTI_SUPER_WACKY_COMPRESSION
411 #define MAX_CODE ( ( 1 << BITS ) - 1 )
412 #define TABLE_SIZE 35023L
413 #define END_OF_STREAM 256
414 #define BUMP_CODE 257
415 #define FLUSH_CODE 258
416 #define FIRST_CODE 259
425 static DICTIONARY dict[TABLE_SIZE];
426 static char decode_stack[TABLE_SIZE];
427 static uint next_code;
428 static int current_code_bits;
429 static uint next_bump_code;
431 typedef struct BitBuf {
437 void output_bits( BitBuf *bitbuf, uint code, int count )
441 mask = 1L << ( count - 1 );
444 bitbuf->rack |= bitbuf->mask;
446 if ( bitbuf->mask == 0 ) {
447 *bitbuf->data++=(ubyte)bitbuf->rack;
455 uint input_bits( BitBuf *bitbuf, int bit_count )
460 mask = 1L << ( bit_count - 1 );
463 if ( bitbuf->mask == 0x80 ) {
464 bitbuf->rack = *bitbuf->data++;
465 if ( bitbuf->rack == EOF )
466 return END_OF_STREAM;
468 if ( bitbuf->rack & bitbuf->mask )
469 return_value |= mask;
472 if ( bitbuf->mask == 0 )
475 return( return_value );
479 static void InitializeDictionary()
483 for ( i = 0 ; i < TABLE_SIZE ; i++ )
484 dict[i].code_value = UNUSED;
486 next_code = FIRST_CODE;
487 current_code_bits = 9;
488 next_bump_code = 511;
492 static uint find_child_node( int parent_code, int child_character )
497 index = ( child_character << ( BITS - 8 ) ) ^ parent_code;
501 offset = TABLE_SIZE - index;
503 if ( dict[ index ].code_value == UNUSED )
504 return( (uint) index );
505 if ( dict[ index ].parent_code == parent_code &&
506 dict[ index ].character == (char) child_character )
508 if ( (int) index >= offset )
511 index += TABLE_SIZE - offset;
516 static uint decode_string( uint count, uint code )
518 while ( code > 255 ) {
519 decode_stack[ count++ ] = dict[ code ].character;
520 code = dict[ code ].parent_code;
522 decode_stack[ count++ ] = (char) code;
526 int lzw_compress( ubyte *outputbuf, ubyte *inputbuf, int input_size )
534 // Init output bit buffer
537 output.data = outputbuf;
539 InitializeDictionary();
541 string_code = *inputbuf++;
543 for ( i=1 ; i<input_size ; i++ ) {
544 character = *inputbuf++;
545 index = find_child_node( string_code, character );
546 if ( dict[ index ].code_value != - 1 )
547 string_code = dict[ index ].code_value;
549 dict[ index ].code_value = next_code++;
550 dict[ index ].parent_code = string_code;
551 dict[ index ].character = (char) character;
552 output_bits( &output, (unsigned long) string_code, current_code_bits );
553 string_code = character;
554 if ( next_code > MAX_CODE ) {
555 output_bits( &output, (unsigned long) FLUSH_CODE, current_code_bits );
556 InitializeDictionary();
557 } else if ( next_code > next_bump_code ) {
558 output_bits( &output, (unsigned long) BUMP_CODE, current_code_bits );
560 next_bump_code <<= 1;
565 output_bits( &output, (unsigned long) string_code, current_code_bits );
566 output_bits( &output, (unsigned long) END_OF_STREAM, current_code_bits);
568 if ( output.mask != 0x80 )
569 *output.data++ = (ubyte)output.rack;
571 return output.data-outputbuf;
575 int lzw_expand( ubyte *outputbuf, ubyte *inputbuf )
586 input.data = inputbuf;
590 InitializeDictionary();
591 old_code = (uint) input_bits( &input, current_code_bits );
592 if ( old_code == END_OF_STREAM )
594 character = old_code;
595 outputbuf[counter++] = ( ubyte )old_code;
597 new_code = (uint) input_bits( &input, current_code_bits );
598 if ( new_code == END_OF_STREAM )
600 if ( new_code == FLUSH_CODE )
602 if ( new_code == BUMP_CODE ) {
606 if ( new_code >= next_code ) {
607 decode_stack[ 0 ] = (char) character;
608 count = decode_string( 1, old_code );
610 count = decode_string( 0, new_code );
612 character = decode_stack[ count - 1 ];
614 outputbuf[counter++] = ( ubyte )decode_stack[ --count ];
615 dict[ next_code ].parent_code = old_code;
616 dict[ next_code ].character = (char) character;
624 // send the specified data packet to all players
625 void multi_io_send(net_player *pl, ubyte *data, int len)
628 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
632 // don't do it for single player
633 if(!(Game_mode & GM_MULTIPLAYER)){
638 if(MULTIPLAYER_CLIENT){
639 // Assert(pl == Net_player);
640 if(pl != Net_player){
644 // Assert(pl != Net_player);
645 if(pl == Net_player){
650 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
651 if ((pl->s_info.unreliable_buffer_size + len) > MAX_PACKET_SIZE) {
652 multi_io_send_force(pl);
653 pl->s_info.unreliable_buffer_size = 0;
656 Assert((pl->s_info.unreliable_buffer_size + len) <= MAX_PACKET_SIZE);
658 memcpy(pl->s_info.unreliable_buffer + pl->s_info.unreliable_buffer_size, data, len);
659 pl->s_info.unreliable_buffer_size += len;
662 void multi_io_send_to_all(ubyte *data, int length, net_player *ignore)
665 Assert(MULTIPLAYER_MASTER);
667 // need to check for i > 1, hmmm... and connected. I don't know.
668 for (i = 0; i < MAX_PLAYERS; i++ ) {
669 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
673 // maybe ignore a player
674 if((ignore != NULL) && (&Net_players[i] == ignore)){
678 // ingame joiners not waiting to select a ship doesn't get any packets
679 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
684 multi_io_send(&Net_players[i], data, length);
688 void multi_io_send_force(net_player *pl)
691 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
695 // don't do it for single player
696 if(!(Game_mode & GM_MULTIPLAYER)){
700 // send everything in
701 if (MULTIPLAYER_MASTER) {
702 psnet_send(&pl->p_info.addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
704 // add the bytes sent to this player
705 pl->sv_bytes_sent += pl->s_info.unreliable_buffer_size;
707 psnet_send(&Netgame.server_addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
709 pl->s_info.unreliable_buffer_size = 0;
712 // send the data packet to all players via their reliable sockets
713 void multi_io_send_reliable(net_player *pl, ubyte *data, int len)
716 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
720 // don't do it for single player
721 if(!(Game_mode & GM_MULTIPLAYER)){
726 if(MULTIPLAYER_CLIENT){
727 // Assert(pl == Net_player);
728 if(pl != Net_player){
732 // Assert(pl != Net_player);
733 if(pl == Net_player){
738 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
739 if ((pl->s_info.reliable_buffer_size + len) > MAX_PACKET_SIZE) {
740 multi_io_send_reliable_force(pl);
741 pl->s_info.reliable_buffer_size = 0;
744 Assert((pl->s_info.reliable_buffer_size + len) <= MAX_PACKET_SIZE);
746 memcpy(pl->s_info.reliable_buffer + pl->s_info.reliable_buffer_size, data, len);
747 pl->s_info.reliable_buffer_size += len;
750 void multi_io_send_to_all_reliable(ubyte* data, int length, net_player *ignore)
753 Assert(MULTIPLAYER_MASTER);
755 // need to check for i > 1, hmmm... and connected. I don't know.
756 for (i = 0; i < MAX_PLAYERS; i++ ) {
757 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
761 // maybe ignore a player
762 if((ignore != NULL) && (&Net_players[i] == ignore)){
766 // ingame joiners not waiting to select a ship doesn't get any packets
767 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
772 multi_io_send_reliable(&Net_players[i], data, length);
776 void multi_io_send_reliable_force(net_player *pl)
779 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
783 // don't do it for single player
784 if(!(Game_mode & GM_MULTIPLAYER)){
788 // send everything in
789 if(MULTIPLAYER_MASTER) {
790 psnet_rel_send(pl->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
791 } else if(Net_player != NULL){
792 psnet_rel_send(Net_player->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
794 pl->s_info.reliable_buffer_size = 0;
797 // send all buffered packets
798 void multi_io_send_buffered_packets()
802 // don't do it for single player
803 if(!(Game_mode & GM_MULTIPLAYER)){
808 if(MULTIPLAYER_MASTER){
809 for(idx=0; idx<MAX_PLAYERS; idx++){
810 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
811 // force unreliable data
812 if(Net_players[idx].s_info.unreliable_buffer_size > 0){
813 multi_io_send_force(&Net_players[idx]);
814 Net_players[idx].s_info.unreliable_buffer_size = 0;
817 // force reliable data
818 if(Net_players[idx].s_info.reliable_buffer_size > 0){
819 multi_io_send_reliable_force(&Net_players[idx]);
820 Net_players[idx].s_info.reliable_buffer_size = 0;
826 else if(Net_player != NULL){
827 // force unreliable data
828 if(Net_player->s_info.unreliable_buffer_size > 0){
829 multi_io_send_force(Net_player);
830 Net_player->s_info.unreliable_buffer_size = 0;
833 // force reliable data
834 if(Net_player->s_info.reliable_buffer_size > 0){
835 multi_io_send_reliable_force(Net_player);
836 Net_player->s_info.reliable_buffer_size = 0;
841 // 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)
842 void send_game_chat_packet(net_player *from, char *msg, int msg_mode, net_player *to, char *expr, int server_msg)
844 ubyte data[MAX_PACKET_SIZE],mode;
847 BUILD_HEADER(GAME_CHAT);
850 ADD_DATA(from->player_id);
852 // add the message mode and if in MSG_TARGET mode, add who the target is
853 ADD_DATA(server_msg);
854 mode = (ubyte)msg_mode;
857 case MULTI_MSG_TARGET:
859 ADD_DATA(to->player_id);
862 Assert(expr != NULL);
866 // add the message itself
869 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
871 // message all players
873 for(idx=0;idx<MAX_PLAYERS;idx++){
874 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from)){
875 multi_io_send_reliable(&Net_players[idx], data, packet_size);
880 // message only friendly players
881 case MULTI_MSG_FRIENDLY:
882 for(idx=0;idx<MAX_PLAYERS;idx++){
883 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)){
884 multi_io_send_reliable(&Net_players[idx], data, packet_size);
889 // message only hostile players
890 case MULTI_MSG_HOSTILE:
891 for(idx=0;idx<MAX_PLAYERS;idx++){
892 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)){
893 multi_io_send_reliable(&Net_players[idx], data, packet_size);
898 // message the player's target
899 case MULTI_MSG_TARGET:
901 if(MULTI_CONNECTED((*to)) && !MULTI_STANDALONE((*to))){
902 multi_io_send_reliable(to, data, packet_size);
906 // message all players who match the expression string
908 Assert(expr != NULL);
909 for(idx=0;idx<MAX_PLAYERS;idx++){
910 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && multi_msg_matches_expr(&Net_players[idx],expr) ){
911 multi_io_send_reliable(&Net_players[idx], data, packet_size);
917 // send to the server, who will take care of routing it
919 multi_io_send_reliable(Net_player, data, packet_size);
923 // process a general game chat packet, if we're the standalone we should rebroadcast
924 void process_game_chat_packet( ubyte *data, header *hinfo )
928 int color_index,player_index,to_player_index,should_display,server_msg;
929 char msg[MULTI_MSG_MAX_TEXT_LEN+CALLSIGN_LEN+2];
933 offset = HEADER_LENGTH;
935 // get the id of the sender
938 // determine if this is a server message
939 GET_DATA(server_msg);
944 // if targeting a specific player, get the address
947 case MULTI_MSG_TARGET:
954 // get the message itself
958 // get the index of the sending player
959 color_index = find_player_id(from);
960 player_index = color_index;
962 // if we couldn't find the player - bail
963 if(player_index == -1){
964 nprintf(("Network","Could not find player for processing game chat packet!\n"));
970 // if we're the server, determine what to do with the packet here
971 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
972 // if he's targeting a specific player, find out who it is
973 if(mode == MULTI_MSG_TARGET){
974 to_player_index = find_player_id(to);
976 to_player_index = -1;
979 // if we couldn't find who sent the message or who should be getting the message, the bail
980 if(((to_player_index == -1) && (mode == MULTI_MSG_TARGET)) || (player_index == -1)){
984 // determine if _I_ should be seeing the text
985 if(Game_mode & GM_STANDALONE_SERVER){
988 // check against myself for several specific cases
990 if((mode == MULTI_MSG_ALL) ||
991 ((mode == MULTI_MSG_FRIENDLY) && (Net_player->p_info.team == Net_players[player_index].p_info.team)) ||
992 ((mode == MULTI_MSG_HOSTILE) && (Net_player->p_info.team != Net_players[player_index].p_info.team)) ||
993 ((mode == MULTI_MSG_TARGET) && (MY_NET_PLAYER_NUM == to_player_index)) ||
994 ((mode == MULTI_MSG_EXPR) && multi_msg_matches_expr(Net_player,expr)) ){
999 // if we're the server of a game, we need to rebroadcast to all other players
1001 // individual target mission
1002 case MULTI_MSG_TARGET:
1003 // if I was the inteneded target, or we couldn't find the intended target, don't rebroadcast
1004 if(to_player_index != MY_NET_PLAYER_NUM){
1005 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, &Net_players[to_player_index], NULL, server_msg);
1009 case MULTI_MSG_EXPR:
1010 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, expr, server_msg);
1014 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, NULL, server_msg);
1018 // if a client receives this packet, its always ok for him to display it
1023 // if we're not on a standalone
1025 if(server_msg == 2){
1028 multi_display_chat_msg(msg, player_index, !server_msg);
1033 // broadcast a hud message to all players
1034 void send_hud_msg_to_all( char* msg )
1036 ubyte data[MAX_PACKET_SIZE];
1039 // only the server should be sending this packet
1040 BUILD_HEADER(HUD_MSG);
1044 multi_io_send_to_all( data, packet_size );
1047 // process an incoming hud message packet
1048 void process_hud_message(ubyte* data, header* hinfo)
1051 char msg_buffer[255];
1053 offset = HEADER_LENGTH;
1055 GET_STRING(msg_buffer);
1058 // this is the only safe place to do this since only in the mission is the HUD guaranteed to be inited
1059 if(Game_mode & GM_IN_MISSION){
1060 HUD_printf(msg_buffer);
1064 // send a join packet request to the specified address (should be a server)
1065 void send_join_packet(net_addr* addr,join_request *jr)
1067 ubyte data[MAX_PACKET_SIZE];
1070 // build the header and add the request
1074 psnet_send(addr, data, packet_size);
1077 // process an incoming join request packet
1078 void process_join_packet(ubyte* data, header* hinfo)
1083 int host_restr_mode;
1084 // int team0_avail,team1_avail;
1085 char join_string[255];
1088 // only the server of the game should ever receive this packet
1089 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
1092 offset = HEADER_LENGTH;
1094 // read in the request info
1095 memset(&jr,0,sizeof(join_request));
1101 // fill in the address information of where this came from
1102 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
1104 // determine if we should accept this guy, or return a reason we should reject him
1105 // see the DENY_* codes in multi.h
1106 ret_code = multi_eval_join_request(&jr,&addr);
1108 // evaluate the return code
1110 // he should be accepted
1114 // we have to query the host because this is a restricted game
1115 case JOIN_QUERY_RESTRICTED :
1116 if(!(Game_mode & GM_STANDALONE_SERVER)){
1117 // notify the host of the event
1118 snd_play(&Snds[SND_CUE_VOICE]);
1121 // set the query timestamp
1122 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
1123 Netgame.flags |= NG_FLAG_INGAME_JOINING;
1125 // determine what mode we're in
1126 host_restr_mode = -1;
1127 memset(join_string,0,255);
1128 // if(Netgame.type == NG_TYPE_TEAM){
1129 // multi_player_ships_available(&team0_avail,&team1_avail);
1131 // if(team0_avail && team1_avail){
1132 // host_restr_mode = MULTI_JOIN_RESTR_MODE_4;
1133 // sprintf(join_string,"Player %s has tried to join. Accept on team 1 or 2 ?",jr.callsign);
1134 // } else if(team0_avail && !team1_avail){
1135 // host_restr_mode = MULTI_JOIN_RESTR_MODE_2;
1136 // sprintf(join_string,"Player %s has tried to join team 0, accept y/n ? ?",jr.callsign);
1137 // } else if(!team0_avail && team1_avail){
1138 // host_restr_mode = MULTI_JOIN_RESTR_MODE_3;
1139 // sprintf(join_string,"Player %s has tried to join team 1, accept y/n ?",jr.callsign);
1141 // } else if(Netgame.mode == NG_MODE_RESTRICTED){
1142 host_restr_mode = MULTI_JOIN_RESTR_MODE_1;
1143 sprintf(join_string,XSTR("Player %s has tried to join, accept y/n ?",715),jr.callsign);
1145 Assert(host_restr_mode != -1);
1147 // store the request info
1148 memcpy(&Multi_restr_join_request,&jr,sizeof(join_request));
1149 memcpy(&Multi_restr_addr,&addr,sizeof(net_addr));
1150 Multi_join_restr_mode = host_restr_mode;
1152 // if i'm the standalone server, I need to send a query to the host
1153 if(Game_mode & GM_STANDALONE_SERVER){
1154 send_host_restr_packet(jr.callsign,0,Multi_join_restr_mode);
1156 HUD_printf(join_string);
1160 ml_printf(NOX("Receive restricted join request from %s"), jr.callsign);
1164 // he'e being denied for some reason
1166 // send him the reason he is being denied
1167 send_deny_packet(&addr,ret_code);
1171 // process the rest of the request
1172 multi_process_valid_join_request(&jr,&addr);
1175 // send a notification that a new player has joined the game (if target != NULL, broadcast the packet)
1176 void send_new_player_packet(int new_player_num,net_player *target)
1178 ubyte data[MAX_PACKET_SIZE], val;
1179 int packet_size = 0;
1181 BUILD_HEADER( NOTIFY_NEW_PLAYER );
1183 // add the new player's info
1184 ADD_DATA(new_player_num);
1185 ADD_DATA(Net_players[new_player_num].p_info.addr);
1186 ADD_DATA(Net_players[new_player_num].player_id);
1187 ADD_DATA(Net_players[new_player_num].flags);
1188 ADD_STRING(Net_players[new_player_num].player->callsign);
1189 ADD_STRING(Net_players[new_player_num].player->image_filename);
1190 ADD_STRING(Net_players[new_player_num].player->squad_filename);
1191 ADD_STRING(Net_players[new_player_num].p_info.pxo_squad_name);
1193 val = (ubyte)Net_players[new_player_num].p_info.team;
1196 // broadcast the data
1198 multi_io_send_reliable(target, data, packet_size);
1200 multi_io_send_to_all_reliable(data, packet_size);
1204 // process a notification for a new player who has joined the game
1205 void process_new_player_packet(ubyte* data, header* hinfo)
1207 int already_in_game = 0;
1208 int offset, new_player_num,player_num,new_flags;
1210 char new_player_name[CALLSIGN_LEN+2] = "";
1211 char new_player_image[MAX_FILENAME_LEN+1] = "";
1212 char new_player_squad[MAX_FILENAME_LEN+1] = "";
1213 char new_player_pxo_squad[LOGIN_LEN+1] = "";
1214 char notify_string[256];
1218 offset = HEADER_LENGTH;
1220 // get the new players information
1221 GET_DATA(new_player_num);
1224 GET_DATA(new_flags);
1225 GET_STRING(new_player_name);
1226 GET_STRING(new_player_image);
1227 GET_STRING(new_player_squad);
1228 GET_STRING(new_player_pxo_squad);
1232 player_num = multi_find_open_player_slot();
1233 Assert(player_num != -1);
1235 // note that this new code does not check for duplicate IPs. It merely checks to see if
1236 // the slot referenced by new_player_num is already occupied by a connected player
1237 if(MULTI_CONNECTED(Net_players[new_player_num])){
1241 // if he's not alreayd in the game for one reason or another
1242 if ( !already_in_game ) {
1243 if ( Game_mode & GM_IN_MISSION ){
1244 HUD_sourced_printf(HUD_SOURCE_COMPUTER, XSTR("%s has entered the game\n",716), new_player_name);
1247 // create the player
1248 memcpy(new_addr.net_id, Psnet_my_addr.net_id, 4);
1250 if(new_flags & NETINFO_FLAG_OBSERVER){
1251 multi_obs_create_player(new_player_num,new_player_name,&new_addr,&Players[player_num]);
1252 Net_players[new_player_num].flags |= new_flags;
1254 multi_create_player( new_player_num, &Players[player_num],new_player_name, &new_addr, -1, new_id );
1255 Net_players[new_player_num].flags |= new_flags;
1258 // copy in the filename
1259 if(strlen(new_player_image) > 0){
1260 strcpy(Net_players[new_player_num].player->image_filename, new_player_image);
1262 strcpy(Net_players[new_player_num].player->image_filename, "");
1264 // copy his pilot squad filename
1265 Net_players[new_player_num].player->insignia_texture = -1;
1266 player_set_squad_bitmap(Net_players[new_player_num].player, new_player_squad);
1268 // copy in his pxo squad name
1269 strcpy(Net_players[new_player_num].p_info.pxo_squad_name, new_player_pxo_squad);
1271 // since we just created the player, set the last_heard_time here.
1272 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1274 Net_players[new_player_num].p_info.team = team;
1276 Net_players[new_player_num].player_id = new_id;
1278 // zero out this players ping
1279 multi_ping_reset(&Net_players[new_player_num].s_info.ping);
1281 // add a chat message
1282 if(Net_players[new_player_num].player->callsign != NULL){
1283 sprintf(notify_string,XSTR("<%s has joined>",717),Net_players[new_player_num].player->callsign);
1284 multi_display_chat_msg(notify_string,0,0);
1289 ml_printf(NOX("Received notification of new player %s"), Net_players[new_player_num].player->callsign);
1291 // let the current ui screen know someone joined
1292 switch(gameseq_get_state()){
1293 case GS_STATE_MULTI_HOST_SETUP :
1294 multi_create_handle_join(&Net_players[new_player_num]);
1296 case GS_STATE_MULTI_CLIENT_SETUP :
1297 multi_jw_handle_join(&Net_players[new_player_num]);
1302 #define PLAYER_DATA_SLOP 100
1304 void send_accept_player_data( net_player *npp, int is_ingame )
1308 ubyte data[MAX_PACKET_SIZE], stop;
1310 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1312 // add in the netplayer data for all players
1314 for (i=0; i<MAX_PLAYERS; i++) {
1315 // skip non connected players
1316 if ( !MULTI_CONNECTED(Net_players[i]) ){
1320 // skip this new player's entry
1321 if ( npp->player_id == Net_players[i].player_id ){
1325 // add the stop byte
1328 // add the player's number
1331 // add the player's address
1332 ADD_DATA(Net_players[i].p_info.addr);
1335 ADD_DATA(Net_players[i].player_id);
1338 ADD_STRING(Net_players[i].player->callsign);
1340 // add his image filename
1341 ADD_STRING(Net_players[i].player->image_filename);
1343 // add his squad filename
1344 ADD_STRING(Net_players[i].player->squad_filename);
1346 // add his PXO squad name
1347 ADD_STRING(Net_players[i].p_info.pxo_squad_name);
1350 ADD_DATA(Net_players[i].flags);
1352 // add his object's net sig
1354 ADD_DATA( Objects[Net_players[i].player->objnum].net_signature );
1357 if ( (packet_size + PLAYER_DATA_SLOP) > MAX_PACKET_SIZE ) {
1358 stop = APD_END_PACKET;
1360 multi_io_send_reliable( npp, data, packet_size );
1361 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1367 // add the stop byte
1368 stop = APD_END_DATA;
1370 multi_io_send_reliable(npp, data, packet_size);
1373 // send an accept packet to a client in response to a request to join the game
1374 void send_accept_packet(int new_player_num, int code, int ingame_join_team)
1377 ubyte data[MAX_PACKET_SIZE],val;
1378 char notify_string[256];
1381 Assert(new_player_num >= 0);
1383 // setup his "reliable" socket
1384 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1386 // build the packet header
1388 BUILD_HEADER(ACCEPT);
1390 // add the accept code
1393 // add code specific accept data
1394 if (code & ACCEPT_INGAME) {
1395 // the game filename
1396 ADD_STRING(Game_current_mission_filename);
1398 // if he is joining on a specific team, mark it here
1399 if(ingame_join_team != -1){
1402 val = (ubyte)ingame_join_team;
1410 if (code & ACCEPT_OBSERVER) {
1411 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1414 if (code & ACCEPT_HOST) {
1415 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1418 if (code & ACCEPT_CLIENT) {
1419 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1422 // add the current skill level setting on the host
1423 ADD_DATA(Game_skill_level);
1425 // add this guys player num
1426 ADD_DATA(new_player_num);
1428 // add his player id
1429 ADD_DATA(Net_players[new_player_num].player_id);
1431 // add netgame type flags
1432 ADD_DATA(Netgame.type_flags);
1435 // char buffer[100];
1436 // nprintf(("Network", "About to send accept packet to %s on port %d\n", get_text_address(buffer, addr->addr), addr->port ));
1439 // actually send the packet
1440 psnet_send(&Net_players[new_player_num].p_info.addr, data, packet_size);
1442 // if he's not an observer, inform all the other players in the game about him
1443 // inform the other players in the game about this new player
1444 for (i=0; i<MAX_PLAYERS; i++) {
1445 // skip unconnected players as well as this new guy himself
1446 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])) {
1450 // send the new packet
1451 send_new_player_packet(new_player_num,&Net_players[i]);
1454 // add a chat message
1455 if(Net_players[new_player_num].player->callsign != NULL){
1456 sprintf(notify_string,XSTR("<%s has joined>",717), Net_players[new_player_num].player->callsign);
1457 multi_display_chat_msg(notify_string, 0, 0);
1460 // handle any team vs. team details
1461 if (!(code & ACCEPT_OBSERVER)) {
1462 multi_team_handle_join(&Net_players[new_player_num]);
1466 if(Net_players[new_player_num].tracker_player_id >= 0){
1467 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);
1469 ml_printf(NOX("Server accepted %s as new client"), Net_players[new_player_num].player->callsign);
1474 // process the player data from the server
1475 void process_accept_player_data( ubyte *data, header *hinfo )
1477 int offset, player_num, player_slot_num, new_flags;
1478 char name[CALLSIGN_LEN + 1] = "";
1479 char image_name[MAX_FILENAME_LEN + 1] = "";
1480 char squad_name[MAX_FILENAME_LEN + 1] = "";
1481 char pxo_squad_name[LOGIN_LEN+1] = "";
1485 ushort ig_signature;
1487 offset = HEADER_LENGTH;
1490 while ( stop == APD_NEXT ) {
1491 player_slot_num = multi_find_open_player_slot();
1492 Assert(player_slot_num != -1);
1494 // get the player's number
1495 GET_DATA(player_num);
1497 // add the player's address
1500 // get the player's id#
1501 GET_DATA(player_id);
1506 // add his image filename
1507 GET_STRING(image_name);
1509 // get his squad logo filename
1510 GET_STRING(squad_name);
1512 // get his PXO squad name
1513 GET_STRING(pxo_squad_name);
1516 GET_DATA(new_flags);
1518 if (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) {
1519 if (!multi_obs_create_player(player_num, name, &addr, &Players[player_slot_num])) {
1524 // the error handling here is less than stellar. We should probably put up a popup and go
1525 // back to the main menu. But then again, this should never ever happen!
1526 if ( !multi_create_player(player_num, &Players[player_slot_num],name, &addr, -1, player_id) ) {
1531 // copy his image filename
1532 strcpy(Net_players[player_num].player->image_filename, image_name);
1534 // copy his pilot squad filename
1535 Net_players[player_num].player->insignia_texture = -1;
1536 player_set_squad_bitmap(Net_players[player_num].player, squad_name);
1538 // copy his pxo squad name
1539 strcpy(Net_players[player_num].p_info.pxo_squad_name, pxo_squad_name);
1541 // set his player id#
1542 Net_players[player_num].player_id = player_id;
1544 // mark him as being connected
1545 Net_players[player_num].flags |= NETINFO_FLAG_CONNECTED;
1546 Net_players[player_num].flags |= new_flags;
1548 // set the server pointer
1549 if ( Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER ) {
1550 Netgame.server = &Net_players[player_num];
1551 Netgame.server->last_heard_time = timer_get_fixed_seconds();
1553 // also - always set the server address to be where this data came from, NOT from
1554 // the data in the packet
1555 fill_net_addr(&Net_players[player_num].p_info.addr, hinfo->addr, hinfo->net_id, hinfo->port);
1558 // set the host pointer
1559 if ( Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST ) {
1560 Netgame.host = &Net_players[player_num];
1563 // read in the player's object net signature and store as his objnum for now
1564 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME ) {
1565 GET_DATA( ig_signature );
1566 Net_players[player_num].player->objnum = ig_signature;
1569 // get the stop byte
1574 if ( stop == APD_END_DATA ) {
1575 // if joining a game automatically, set the connect address to NULl so we don't try and
1576 // do this next time we enter a game
1577 if (Cmdline_connect_addr != NULL) {
1578 Cmdline_connect_addr = NULL;
1581 // send my stats to the server if I'm not in observer mode
1582 if (!(Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER)) {
1583 send_player_stats_block_packet(Net_player, STATS_ALLTIME);
1586 // if i'm being accepted as a host, then move into the host setup state
1587 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_HOST) {
1588 // set my permission bits
1589 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
1590 Net_player->state = NETPLAYER_STATE_STD_HOST_SETUP;
1592 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
1595 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER) {
1596 Net_player->flags |= NETINFO_FLAG_OBSERVER;
1598 // since observers can join 1 of 2 ways, only do this if we're not doing an ingame observer join
1599 if ( !(Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) ) {
1600 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1604 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_CLIENT) {
1605 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1608 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) {
1609 // flag myself as being an ingame joiner
1610 Net_player->flags |= NETINFO_FLAG_INGAME_JOIN;
1612 // move myself into the ingame join mission sync state
1613 Multi_sync_mode = MULTI_SYNC_INGAME;
1614 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
1617 // update my options on the server
1618 multi_options_update_local();
1620 // if we're in PXO mode, mark it down in our player struct
1621 if(MULTI_IS_TRACKER_GAME){
1622 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1623 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1628 // process an accept packet from the server
1629 extern int Select_default_ship;
1631 void process_accept_packet(ubyte* data, header* hinfo)
1633 int code, my_player_num, offset;
1637 // get the accept code
1638 offset = HEADER_LENGTH;
1642 // read in the accept code specific data
1644 if (code & ACCEPT_INGAME) {
1645 // the game filename
1646 GET_STRING(Game_current_mission_filename);
1647 Select_default_ship = 0;
1649 // determine if I'm being placed on a team
1656 if (code & ACCEPT_OBSERVER) {
1657 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1660 if (code & ACCEPT_HOST) {
1661 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1664 if (code & ACCEPT_CLIENT) {
1665 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1668 // fill in the netgame server address
1669 fill_net_addr( &Netgame.server_addr, hinfo->addr, hinfo->net_id, hinfo->port );
1671 // get the skill level setting
1672 GET_DATA(Game_skill_level);
1674 // get my netplayer number
1675 GET_DATA(my_player_num);
1678 GET_DATA(player_id);
1680 // get netgame type flags
1681 GET_DATA(Netgame.type_flags);
1683 // setup the Net_players structure for myself first
1684 Net_player = &Net_players[my_player_num];
1685 Net_player->flags = 0;
1686 Net_player->tracker_player_id = Multi_tracker_id;
1687 Net_player->player_id = player_id;
1688 Net_player->s_info.xfer_handle = -1;
1689 // stuff_netplayer_info( Net_player, &Psnet_my_addr, Ships[Objects[Player->objnum].instance].ship_info_index, Player );
1690 stuff_netplayer_info( Net_player, &Psnet_my_addr, 0, Player );
1691 multi_options_local_load(&Net_player->p_info.options, Net_player);
1693 Net_player->p_info.team = team;
1696 // determine if I have a CD
1698 Net_player->flags |= NETINFO_FLAG_HAS_CD;
1701 // set accept code in netplayer for this guy
1702 if ( code & ACCEPT_INGAME ){
1703 Net_player->flags |= NETINFO_FLAG_ACCEPT_INGAME;
1705 if ( code & ACCEPT_OBSERVER ){
1706 Net_player->flags |= NETINFO_FLAG_ACCEPT_OBSERVER;
1708 if ( code & ACCEPT_HOST ){
1709 Net_player->flags |= NETINFO_FLAG_ACCEPT_HOST;
1711 if ( code & ACCEPT_CLIENT ){
1712 Net_player->flags |= NETINFO_FLAG_ACCEPT_CLIENT;
1715 // if I have hacked data
1716 if(game_hacked_data()){
1717 Net_player->flags |= NETINFO_FLAG_HAXOR;
1720 // if we're supposed to flush our local data cache, do so now
1721 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
1722 multi_flush_multidata_cache();
1725 Net_player->sv_bytes_sent = 0;
1726 Net_player->sv_last_pl = -1;
1727 Net_player->cl_bytes_recvd = 0;
1728 Net_player->cl_last_pl = -1;
1730 // intiialize endgame stuff
1731 multi_endgame_init();
1735 // make a call to psnet to initialize and try to connect with the server.
1736 psnet_rel_connect_to_server( &Net_player->reliable_socket, &Netgame.server_addr );
1737 if ( Net_player->reliable_socket == INVALID_SOCKET ) {
1738 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONNECT_FAIL);
1742 // send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet)
1743 void send_leave_game_packet(short player_id, int kicked_reason, net_player *target)
1745 ubyte data[MAX_PACKET_SIZE];
1747 int packet_size = 0;
1749 BUILD_HEADER(LEAVE_GAME);
1751 // add a flag indicating whether he was kicked or not
1752 val = (char)kicked_reason;
1755 if (player_id < 0) {
1756 ADD_DATA(Net_player->player_id);
1758 // inform the host that we are leaving the game
1759 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1760 multi_io_send_to_all_reliable(data, packet_size);
1762 multi_io_send_reliable(Net_player, data, packet_size);
1765 // this is the case where to server is tossing a player (or indicating a respawned player has quit or become an observer)
1766 // so he has to tell everyone that this guy left
1768 nprintf(("Network","Sending a leave game packet to all players (server)\n"));
1770 // a couple of important checks
1771 Assert(player_id != Net_player->player_id);
1772 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1774 // add the id of the guy to be kicked
1775 ADD_DATA(player_id);
1777 // broadcast to everyone
1778 if (target == NULL) {
1779 multi_io_send_to_all_reliable(data, packet_size);
1781 multi_io_send_reliable(target, data, packet_size);
1786 // process a notification the a player has left the game
1787 void process_leave_game_packet(ubyte* data, header* hinfo)
1795 offset = HEADER_LENGTH;
1797 // get whether he was kicked
1798 GET_DATA(kicked_reason);
1800 // get the address of the guy who is to leave
1801 GET_DATA(deader_id);
1804 // determine who is dropping and printf out a notification
1805 player_num = find_player_id(deader_id);
1806 if (player_num == -1) {
1807 nprintf(("Network", "Received leave game packet for unknown player, ignoring\n"));
1811 nprintf(("Network", "Received a leave game notice for %s\n", Net_players[player_num].player->callsign));
1814 // a hook to display that a player was kicked
1815 if (kicked_reason >= 0){
1816 // if it was me that was kicked, leave the game
1817 if((Net_player != NULL) && (Net_player->player_id == deader_id)){
1820 switch(kicked_reason){
1821 case KICK_REASON_BAD_XFER:
1822 notify_code = MULTI_END_NOTIFY_KICKED_BAD_XFER;
1824 case KICK_REASON_CANT_XFER:
1825 notify_code = MULTI_END_NOTIFY_KICKED_CANT_XFER;
1827 case KICK_REASON_INGAME_ENDED:
1828 notify_code = MULTI_END_NOTIFY_KICKED_INGAME_ENDED;
1831 notify_code = MULTI_END_NOTIFY_KICKED;
1835 multi_quit_game(PROMPT_NONE, notify_code);
1838 // otherwise indicate someone was kicked
1840 nprintf(("Network","%s was kicked\n",Net_players[player_num].player->callsign));
1842 // display the result
1843 memset(str, 0, 512);
1844 multi_kick_get_text(&Net_players[player_num], kicked_reason, str);
1845 multi_display_chat_msg(str, player_num, 0);
1849 // first of all, if we're the master, we should be rebroadcasting this packet
1850 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1853 sprintf(msg, XSTR("%s has left the game",719), Net_players[player_num].player->callsign );
1855 if (!(Game_mode & GM_STANDALONE_SERVER)){
1856 HUD_sourced_printf(HUD_SOURCE_HIDDEN, msg);
1859 send_hud_msg_to_all(msg);
1860 multi_io_send_to_all_reliable(data, offset);
1863 // leave the game if the host and/or master has dropped
1865 if (((Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER) || (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)) ) {
1866 nprintf(("Network","Host and/or server has left the game - aborting...\n"));
1869 ml_string(NOX("Host and/or server has left the game"));
1871 // if the host leaves in the debriefing state, we should still wait until the player selects accept before we quit
1872 if (gameseq_get_state() != GS_STATE_DEBRIEF) {
1873 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
1876 delete_player(player_num);
1879 delete_player(player_num);
1881 // OSAPI GUI stuff (if standalone)
1882 if (Game_mode & GM_STANDALONE_SERVER) {
1883 // returns true if we should reset the standalone
1884 if (std_remove_player(&Net_players[player_num])) {
1885 nprintf(("Network", "Should reset!!\n"));
1889 // update these gui vals
1890 std_connect_set_host_connect_status();
1891 std_connect_set_connect_count();
1895 // send information about this currently active game to the specified address
1896 void send_game_active_packet(net_addr* addr)
1900 ubyte data[MAX_PACKET_SIZE],val;
1902 // build the header and add the data
1903 BUILD_HEADER(GAME_ACTIVE);
1905 // add the server version and compatible version #
1906 val = MULTI_FS_SERVER_VERSION;
1908 val = MULTI_FS_SERVER_COMPATIBLE_VERSION;
1911 ADD_STRING(Netgame.name);
1912 ADD_STRING(Netgame.mission_name);
1913 ADD_STRING(Netgame.title);
1914 val = (ubyte)multi_num_players();
1917 // add the proper flags
1919 if((Netgame.mode == NG_MODE_PASSWORD) || ((Game_mode & GM_STANDALONE_SERVER) && (multi_num_players() == 0) && (std_is_host_passwd()))){
1920 flags |= AG_FLAG_PASSWD;
1923 // proper netgame type flags
1924 if(Netgame.type_flags & NG_TYPE_TEAM){
1925 flags |= AG_FLAG_TEAMS;
1926 } else if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
1927 flags |= AG_FLAG_DOGFIGHT;
1929 flags |= AG_FLAG_COOP;
1932 // proper netgame state flags
1933 switch(Netgame.game_state){
1934 case NETGAME_STATE_FORMING:
1935 flags |= AG_FLAG_FORMING;
1938 case NETGAME_STATE_BRIEFING:
1939 case NETGAME_STATE_MISSION_SYNC:
1940 case NETGAME_STATE_HOST_SETUP:
1941 flags |= AG_FLAG_BRIEFING;
1944 case NETGAME_STATE_IN_MISSION:
1945 flags |= AG_FLAG_IN_MISSION;
1948 case NETGAME_STATE_PAUSED:
1949 flags |= AG_FLAG_PAUSE;
1952 case NETGAME_STATE_ENDGAME:
1953 case NETGAME_STATE_DEBRIEF:
1954 flags |= AG_FLAG_DEBRIEF;
1958 // if this is a standalone
1959 if(Game_mode & GM_STANDALONE_SERVER){
1960 flags |= AG_FLAG_STANDALONE;
1963 // if we're in campaign mode
1964 if(Netgame.campaign_mode == MP_CAMPAIGN){
1965 flags |= AG_FLAG_CAMPAIGN;
1968 // add the data about the connection speed of the host machine
1969 Assert( (Multi_connection_speed >= 0) && (Multi_connection_speed <= 4) );
1970 flags |= (Multi_connection_speed << AG_FLAG_CONNECTION_BIT);
1975 psnet_send(addr, data, packet_size);
1978 // process information about an active game
1979 void process_game_active_packet(ubyte* data, header* hinfo)
1984 int modes_compatible;
1986 fill_net_addr(&ag.server_addr, hinfo->addr, hinfo->net_id, hinfo->port);
1988 // read this game into a temporary structure
1989 offset = HEADER_LENGTH;
1991 // get the server version and compatible version
1992 GET_DATA(ag.version);
1993 GET_DATA(ag.comp_version);
1995 GET_STRING(ag.name);
1996 GET_STRING(ag.mission_name);
1997 GET_STRING(ag.title);
1999 ag.num_players = val;
2004 modes_compatible = 1;
2006 if((ag.flags & AG_FLAG_TRACKER) && !Multi_options_g.pxo){
2007 modes_compatible = 0;
2009 if(!(ag.flags & AG_FLAG_TRACKER) && Multi_options_g.pxo){
2010 modes_compatible = 0;
2014 // if this is a compatible version, and our modes are compatible, register it
2015 if( (ag.version == MULTI_FS_SERVER_VERSION) && modes_compatible ){
2016 multi_update_active_games(&ag);
2020 // send_game_update_packet sends an updated Netgame structure to all players currently connected. The update
2021 // is used to change the current mission, current state, etc.
2022 void send_netgame_update_packet(net_player *pl)
2026 ubyte data[MAX_PACKET_SIZE];
2029 BUILD_HEADER(GAME_UPDATE);
2031 // with new mission description field, this becomes way to large
2032 // so we must add every element piece by piece except the
2033 ADD_STRING(Netgame.name);
2034 ADD_STRING(Netgame.mission_name);
2035 ADD_STRING(Netgame.title);
2036 ADD_STRING(Netgame.campaign_name);
2037 ADD_DATA(Netgame.campaign_mode);
2038 ADD_DATA(Netgame.max_players);
2039 ADD_DATA(Netgame.security);
2040 ADD_DATA(Netgame.respawn);
2041 ADD_DATA(Netgame.flags);
2042 ADD_DATA(Netgame.type_flags);
2043 ADD_DATA(Netgame.version_info);
2044 ADD_DATA(Netgame.debug_flags);
2046 // only the server should ever send the netgame state (standalone situation)
2047 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2048 ADD_DATA(Netgame.game_state);
2051 // if we're the host on a standalone, send to the standalone and let him rebroadcast
2052 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2054 multi_io_send_to_all_reliable(data, packet_size);
2056 for(idx=0; idx<MAX_PLAYERS; idx++){
2057 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
2058 send_netgame_descript_packet(&Net_players[idx].p_info.addr, 1);
2062 multi_io_send_reliable(pl, data, packet_size);
2063 send_netgame_descript_packet( &pl->p_info.addr , 1 );
2066 Assert( pl == NULL ); // I don't think that a host in a standalone game would get here.
2067 multi_io_send_reliable(Net_player, data, packet_size);
2070 // host should always send a netgame options update as well
2071 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2072 multi_options_update_netgame();
2076 // process information about the netgame sent from the server/host
2077 void process_netgame_update_packet( ubyte *data, header *hinfo )
2079 int offset,old_flags;
2082 Assert(!(Game_mode & GM_STANDALONE_SERVER));
2083 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
2085 // read in the netgame information
2086 offset = HEADER_LENGTH;
2087 GET_STRING(Netgame.name);
2088 GET_STRING(Netgame.mission_name);
2089 GET_STRING(Netgame.title);
2090 GET_STRING(Netgame.campaign_name);
2091 GET_DATA(Netgame.campaign_mode);
2092 GET_DATA(Netgame.max_players); // ignore on the standalone, who keeps track of this himself
2093 GET_DATA(Netgame.security);
2094 GET_DATA(Netgame.respawn);
2096 // be sure not to blast the quitting flag because of the "one frame extra" problem
2097 old_flags = Netgame.flags;
2098 GET_DATA(Netgame.flags);
2099 GET_DATA(Netgame.type_flags);
2100 GET_DATA(Netgame.version_info);
2101 GET_DATA(Netgame.debug_flags);
2108 // now compare the passed in game state to our current known state. If it has changed, then maybe
2109 // do something interesting.
2110 // move from the forming or debriefing state to the mission sync state
2111 if ( ng_state == NETGAME_STATE_MISSION_SYNC ){
2112 // if coming from the forming state
2113 if( (Netgame.game_state == NETGAME_STATE_FORMING) ||
2114 ((Netgame.game_state != NETGAME_STATE_FORMING) && ((gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP) || (gameseq_get_state() == GS_STATE_MULTI_CLIENT_SETUP))) ){
2115 // do any special processing for forced state transitions
2116 multi_handle_state_special();
2118 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2119 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2120 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2122 // if coming from the debriefing state
2123 else if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2124 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2126 // do any special processing for forced state transitions
2127 multi_handle_state_special();
2129 multi_flush_mission_stuff();
2131 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2132 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2133 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2136 // move from mission sync to team select
2137 else if ( ng_state == NETGAME_STATE_BRIEFING ){
2138 if( (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ||
2139 ((Netgame.game_state != NETGAME_STATE_MISSION_SYNC) && (gameseq_get_state() == GS_STATE_MULTI_MISSION_SYNC) && (Multi_sync_mode != MULTI_SYNC_POST_BRIEFING)) ){
2141 // do any special processing for forced state transitions
2142 multi_handle_state_special();
2144 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2145 gameseq_post_event(GS_EVENT_START_BRIEFING);
2148 // move from the debriefing to the create game screen
2149 else if ( ng_state == NETGAME_STATE_FORMING ){
2150 if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2151 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2152 // do any special processing for forced state transitions
2153 multi_handle_state_special();
2155 multi_flush_mission_stuff();
2157 // move to the proper screen
2158 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2159 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2161 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
2166 Netgame.game_state = ng_state;
2169 // send a request or a reply for mission description, if code == 0, request, if code == 1, reply
2170 void send_netgame_descript_packet(net_addr *addr, int code)
2172 ubyte data[MAX_PACKET_SIZE],val;
2174 int packet_size = 0;
2177 BUILD_HEADER(UPDATE_DESCRIPT);
2183 // add as much of the description as we dare
2184 len = strlen(The_mission.mission_desc);
2185 if(len > MAX_PACKET_SIZE - 10){
2186 len = MAX_PACKET_SIZE - 10;
2188 memcpy(data+packet_size,The_mission.mission_desc,len);
2191 ADD_STRING(The_mission.mission_desc);
2195 Assert(addr != NULL);
2197 psnet_send(addr, data, packet_size);
2201 // process an incoming netgame description packet
2202 void process_netgame_descript_packet( ubyte *data, header *hinfo )
2206 char mission_desc[MISSION_DESC_LENGTH+2];
2209 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2211 // read this game into a temporary structure
2212 offset = HEADER_LENGTH;
2215 // if this is a request for mission description
2217 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2222 // send an update to this guy
2223 send_netgame_descript_packet(&addr, 1);
2225 memset(mission_desc,0,MISSION_DESC_LENGTH+2);
2226 GET_STRING(mission_desc);
2228 // only display if we're in the proper state
2229 state = gameseq_get_state();
2231 case GS_STATE_MULTI_JOIN_GAME:
2232 case GS_STATE_MULTI_CLIENT_SETUP:
2233 case GS_STATE_MULTI_HOST_SETUP:
2234 multi_common_set_text(mission_desc);
2242 // broadcast a query for active games. IPX will use net broadcast and TCP will either request from the MT or from the specified list
2243 void broadcast_game_query()
2247 server_item *s_moveup;
2248 ubyte data[MAX_PACKET_SIZE];
2250 BUILD_HEADER(GAME_QUERY);
2252 // go through the server list and query each of those as well
2253 s_moveup = Game_server_head;
2254 if(s_moveup != NULL){
2256 send_server_query(&s_moveup->server_addr);
2257 s_moveup = s_moveup->next;
2258 } while(s_moveup != Game_server_head);
2261 fill_net_addr(&addr, Psnet_my_addr.addr, Psnet_my_addr.net_id, DEFAULT_GAME_PORT);
2263 // send out a broadcast if our options allow us
2264 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2265 psnet_broadcast( &addr, data, packet_size);
2269 // send an individual query to an address to see if there is an active game
2270 void send_server_query(net_addr *addr)
2273 ubyte data[MAX_PACKET_SIZE];
2275 // build the header and send the data
2276 BUILD_HEADER(GAME_QUERY);
2277 psnet_send(addr, data, packet_size);
2280 // process a query from a client looking for active freespace games
2281 void process_game_query(ubyte* data, header* hinfo)
2286 offset = HEADER_LENGTH;
2290 // check to be sure that we don't capture our own broadcast message
2291 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2292 if ( psnet_same( &addr, &Psnet_my_addr) ){
2296 // if I am not a server of a game, don't send a reply!!!
2297 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
2301 // if the game options are being selected, then ignore the request
2302 // also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
2303 if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
2307 // send information about this active game
2308 send_game_active_packet(&addr);
2311 // sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
2312 void send_netplayer_update_packet( net_player *pl )
2314 int packet_size,idx;
2315 ubyte data[MAX_PACKET_SIZE],val;
2317 BUILD_HEADER(NETPLAYER_UPDATE);
2319 // if I'm the server of the game, I should send an update for _all_players in the game
2320 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2321 for(idx=0;idx<MAX_PLAYERS;idx++){
2322 // only send info for connected players
2323 if(MULTI_CONNECTED(Net_players[idx])){
2328 // add the net player's information
2329 ADD_DATA(Net_players[idx].player_id);
2330 ADD_DATA(Net_players[idx].state);
2331 ADD_DATA(Net_players[idx].p_info.ship_class);
2332 ADD_DATA(Net_players[idx].tracker_player_id);
2334 if(Net_players[idx].flags & NETINFO_FLAG_HAS_CD){
2342 // add the final stop byte
2346 // broadcast the packet
2347 if(!(Game_mode & GM_IN_MISSION)){
2349 multi_io_send_to_all_reliable(data, packet_size);
2351 multi_io_send_reliable(pl, data, packet_size);
2355 multi_io_send_to_all(data, packet_size);
2357 multi_io_send(pl, data, packet_size);
2365 // add my current state in the netgame to this packet
2366 ADD_DATA(Net_player->player_id);
2367 ADD_DATA(Net_player->state);
2368 ADD_DATA(Net_player->p_info.ship_class);
2369 ADD_DATA(Multi_tracker_id);
2371 // add if I have a CD or not
2379 // add a final stop byte
2383 // send the packet to the server
2384 Assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
2385 if(!(Game_mode & GM_IN_MISSION)){
2386 multi_io_send_reliable(Net_player, data, packet_size);
2388 multi_io_send(Net_player, data, packet_size);
2393 // process an incoming netplayer state update. if we're the server, we should rebroadcast
2394 void process_netplayer_update_packet( ubyte *data, header *hinfo )
2396 int offset, player_num;
2402 offset = HEADER_LENGTH;
2404 // get the first stop byte
2407 while(stop != 0xff){
2408 // look the player up
2409 GET_DATA(player_id);
2410 player_num = find_player_id(player_id);
2411 // if we couldn't find him, read in the bogus data
2412 if((player_num == -1) || (Net_player == &Net_players[player_num])){
2413 GET_DATA(bogus.state);
2414 GET_DATA(bogus.p_info.ship_class);
2415 GET_DATA(bogus.tracker_player_id);
2419 // otherwise read in the data correctly
2421 GET_DATA(new_state);
2422 GET_DATA(Net_players[player_num].p_info.ship_class);
2423 GET_DATA(Net_players[player_num].tracker_player_id);
2426 Net_players[player_num].flags |= NETINFO_FLAG_HAS_CD;
2428 Net_players[player_num].flags &= ~(NETINFO_FLAG_HAS_CD);
2431 // if he's changing state to joined, send a team update
2432 if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
2433 multi_team_send_update();
2437 Net_players[player_num].state = new_state;
2440 // get the next stop byte
2446 // if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
2447 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2448 send_netplayer_update_packet(NULL);
2451 // if i'm the standalone and this is an update from the host, maybe change some netgame settings
2452 if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
2453 switch(Net_players[player_num].state){
2454 case NETPLAYER_STATE_STD_HOST_SETUP:
2455 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2458 case NETPLAYER_STATE_HOST_SETUP:
2459 // check for race conditions
2460 if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
2461 Netgame.game_state = NETGAME_STATE_FORMING;
2468 #define EXTRA_DEATH_VAPORIZED (1<<0)
2469 #define EXTRA_DEATH_WASHED (1<<1)
2470 // send a packet indicating a ship has been killed
2471 void send_ship_kill_packet( object *objp, object *other_objp, float percent_killed, int self_destruct )
2473 int packet_size, model;
2474 ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
2475 ushort debris_signature;
2479 // only sendable from the master
2480 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
2483 vaporized = ( (Ships[objp->instance].flags & SF_VAPORIZE) > 0 );
2485 extra_death_info = 0;
2487 extra_death_info |= EXTRA_DEATH_VAPORIZED;
2490 if ( Ships[objp->instance].wash_killed ) {
2491 extra_death_info |= EXTRA_DEATH_WASHED;
2494 // find out the next network signature that will be used for the debris pieces.
2495 model = Ships[objp->instance].modelnum;
2496 pm = model_get(model);
2497 debris_signature = 0;
2498 if ( pm && !vaporized ) {
2499 debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
2500 multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
2501 Ships[objp->instance].arrival_distance = debris_signature;
2504 BUILD_HEADER(SHIP_KILL);
2505 ADD_DATA(objp->net_signature);
2507 // ships which are initially killed get the rest of the data sent. self destructed ships and
2508 if ( other_objp == NULL ) {
2513 nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
2515 ADD_DATA( other_objp->net_signature );
2518 ADD_DATA( debris_signature );
2519 ADD_DATA( percent_killed );
2520 sd = (ubyte)self_destruct;
2522 ADD_DATA( extra_death_info );
2524 // if the ship who died is a player, then send some extra info, like who killed him, etc.
2526 if ( objp->flags & OF_PLAYER_SHIP ) {
2530 pnum = multi_find_player_by_object( objp );
2533 ADD_DATA( was_player );
2535 Assert(Net_players[pnum].player->killer_objtype < CHAR_MAX);
2536 temp = (char)Net_players[pnum].player->killer_objtype;
2539 Assert(Net_players[pnum].player->killer_species < CHAR_MAX);
2540 temp = (char)Net_players[pnum].player->killer_species;
2543 Assert(Net_players[pnum].player->killer_weapon_index < CHAR_MAX);
2544 temp = (char)Net_players[pnum].player->killer_weapon_index;
2547 ADD_STRING( Net_players[pnum].player->killer_parent_name );
2549 ADD_DATA( was_player );
2552 ADD_DATA( was_player );
2555 // send the packet reliably!!!
2556 multi_io_send_to_all_reliable(data, packet_size);
2559 // process a packet indicating that a ship has been killed
2560 void process_ship_kill_packet( ubyte *data, header *hinfo )
2563 ushort ship_sig, other_sig, debris_sig;
2564 object *sobjp, *oobjp;
2565 float percent_killed;
2566 ubyte was_player, extra_death_info, sd;
2567 char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = SPECIES_TERRAN, killer_weapon_index = -1;
2569 offset = HEADER_LENGTH;
2572 GET_DATA( other_sig );
2573 GET_DATA( debris_sig );
2574 GET_DATA( percent_killed );
2576 GET_DATA( extra_death_info );
2577 GET_DATA( was_player );
2580 // pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
2581 if ( was_player != 0 ) {
2582 GET_DATA( killer_objtype );
2583 GET_DATA( killer_species );
2584 GET_DATA( killer_weapon_index );
2585 GET_STRING( killer_name );
2590 sobjp = multi_get_network_object( ship_sig );
2592 // if I am unable to find the ship object which was killed, I have to bail and rely on getting
2593 // another message from the server that this happened!
2594 if ( sobjp == NULL ) {
2595 nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
2599 // set this ship's hull value to 0
2600 sobjp->hull_strength = 0.0f;
2602 // maybe set vaporized
2603 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2604 Ships[sobjp->instance].flags |= SF_VAPORIZE;
2607 // maybe set wash_killed
2608 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2609 Ships[sobjp->instance].wash_killed = 1;
2612 oobjp = multi_get_network_object( other_sig );
2614 if ( was_player != 0 ) {
2617 pnum = multi_find_player_by_object( sobjp );
2619 Net_players[pnum].player->killer_objtype = killer_objtype;
2620 Net_players[pnum].player->killer_species = killer_species;
2621 Net_players[pnum].player->killer_weapon_index = killer_weapon_index;
2622 strcpy( Net_players[pnum].player->killer_parent_name, killer_name );
2626 // check to see if I need to respawn myself
2627 multi_respawn_check(sobjp);
2629 // store the debris signature in the arrival distance which will never get used for player ships
2630 Ships[sobjp->instance].arrival_distance = debris_sig;
2632 // set this bit so that we don't accidentally start switching targets when we die
2633 if(sobjp == Player_obj){
2634 Game_mode |= GM_DEAD_DIED;
2637 nprintf(("Network", "Killing off %s\n", Ships[sobjp->instance].ship_name));
2639 // do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
2640 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2641 ship_hit_kill( sobjp, oobjp, percent_killed, sd );
2643 extern void ship_destroyed( int shipnum );
2644 ship_destroyed( sobjp->instance );
2645 sobjp->flags |= OF_SHOULD_BE_DEAD;
2646 obj_delete( OBJ_INDEX(sobjp) );
2650 // send a packet indicating a ship should be created
2651 void send_ship_create_packet( object *objp, int is_support )
2654 ubyte data[MAX_PACKET_SIZE];
2656 // We will pass the ship to create by name.
2657 BUILD_HEADER(SHIP_CREATE);
2658 ADD_DATA(objp->net_signature);
2659 ADD_DATA( is_support );
2661 ADD_DATA( objp->pos );
2664 // broadcast the packet
2665 multi_io_send_to_all_reliable(data, packet_size);
2668 // process a packet indicating a ship should be created
2669 void process_ship_create_packet( ubyte *data, header *hinfo )
2671 int offset, objnum, is_support;
2674 vector pos = ZERO_VECTOR;
2676 Assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
2677 offset = HEADER_LENGTH;
2678 GET_DATA(signature);
2679 GET_DATA( is_support );
2686 // find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
2687 if ( !is_support ) {
2688 objp = mission_parse_get_arrival_ship( signature );
2689 if ( objp != NULL ) {
2690 objnum = parse_create_object(objp);
2692 nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
2695 Assert( Arriving_support_ship );
2696 if(Arriving_support_ship == NULL){
2699 Arriving_support_ship->pos = pos;
2700 Arriving_support_ship->net_signature = signature;
2701 objnum = parse_create_object( Arriving_support_ship );
2702 Assert( objnum != -1 );
2704 mission_parse_support_arrived( objnum );
2709 // send a packet indicating a wing of ships should be created
2710 void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
2712 int packet_size, index, ship_instance;
2713 ubyte data[MAX_PACKET_SIZE];
2717 // for creating wing -- we just send the index into the wing array of this wing.
2718 // all players load the same mission, and so their array's should all match. We also
2719 // need to send the signature of the first ship that was created. We can find this by
2720 // looking num_to_create places back in the ship_index field in the wing structure.
2722 index = WING_INDEX(wingp);
2723 ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
2724 signature = Objects[Ships[ship_instance].objnum].net_signature;
2726 BUILD_HEADER( WING_CREATE );
2728 ADD_DATA(num_to_create);
2729 ADD_DATA(signature);
2730 ADD_DATA(pre_create_count);
2731 val = wingp->current_wave - 1;
2734 multi_io_send_to_all_reliable(data, packet_size);
2737 // process a packet saying that a wing should be created
2738 void process_wing_create_packet( ubyte *data, header *hinfo )
2740 int offset, index, num_to_create;
2742 int total_arrived_count, current_wave;
2744 offset = HEADER_LENGTH;
2746 GET_DATA(num_to_create);
2747 GET_DATA(signature);
2748 GET_DATA(total_arrived_count);
2749 GET_DATA(current_wave);
2753 // do a sanity check on the wing to be sure that we are actually working on a valid wing
2754 if ( (index < 0) || (index >= num_wings) || (Wings[index].num_waves == -1) ) {
2755 nprintf(("Network", "invalid index %d for wing create packet\n"));
2758 if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
2759 nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
2764 Wings[index].current_count = 0;
2765 Wings[index].total_arrived_count = total_arrived_count;
2766 Wings[index].current_wave = current_wave;
2768 // set the network signature that was passed. The client should create ships in the same order
2769 // as the server -- so all ships should get the same sigs as assigned by the server. We also
2770 // need to set some timestamps and cues correctly to be sure that these things get created on
2771 // the clients correctly
2772 multi_set_network_signature( signature, MULTI_SIG_SHIP );
2773 parse_wing_create_ships( &Wings[index], num_to_create, 1 );
2776 // packet indicating a ship is departing
2777 void send_ship_depart_packet( object *objp )
2779 ubyte data[MAX_PACKET_SIZE];
2783 signature = objp->net_signature;
2785 BUILD_HEADER(SHIP_DEPART);
2786 ADD_DATA( signature );
2788 multi_io_send_to_all_reliable(data, packet_size);
2791 // process a packet indicating a ship is departing
2792 void process_ship_depart_packet( ubyte *data, header *hinfo )
2798 offset = HEADER_LENGTH;
2799 GET_DATA( signature );
2802 // find the object which is departing
2803 objp = multi_get_network_object( signature );
2804 if ( objp == NULL ) {
2805 nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
2809 // start warping him out
2810 shipfx_warpout_start( objp );
2813 // packet to tell clients cargo of a ship was revealed to all
2814 void send_cargo_revealed_packet( ship *shipp )
2816 ubyte data[MAX_PACKET_SIZE];
2819 // build the header and add the data
2820 BUILD_HEADER(CARGO_REVEALED);
2821 ADD_DATA( Objects[shipp->objnum].net_signature );
2823 // server sends to all players
2824 if(MULTIPLAYER_MASTER){
2825 multi_io_send_to_all_reliable(data, packet_size);
2827 // clients just send to the server
2829 multi_io_send_reliable(Net_player, data, packet_size);
2833 // process a cargo revealed packet
2834 void process_cargo_revealed_packet( ubyte *data, header *hinfo )
2840 offset = HEADER_LENGTH;
2841 GET_DATA(signature);
2844 // get a ship pointer and call the ship function to reveal the cargo
2845 objp = multi_get_network_object( signature );
2846 if ( objp == NULL ) {
2847 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
2851 // Assert( objp->type == OBJ_SHIP );
2852 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2856 // this will take care of re-routing to all other clients
2857 ship_do_cargo_revealed( &Ships[objp->instance], 1);
2859 // server should rebroadcast
2860 if(MULTIPLAYER_MASTER){
2861 send_cargo_revealed_packet(&Ships[objp->instance]);
2865 // defines used for secondary fire packet
2866 #define SFPF_ALLOW_SWARM (1<<7)
2867 #define SFPF_DUAL_FIRE (1<<6)
2868 #define SFPF_TARGET_LOCKED (1<<5)
2870 // send a packet indicating a secondary weapon was fired
2871 void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm )
2873 int packet_size, net_player_num;
2874 ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
2876 ushort target_signature;
2880 // Assert ( starting_count < UCHAR_MAX );
2882 // get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
2883 // we might send the info to the other player different than the one who fired
2884 objp = &Objects[shipp->objnum];
2885 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
2886 if ( num_fired == 0 ) {
2891 aip = &Ai_info[shipp->ai_index];
2893 current_bank = (ubyte)shipp->weapons.current_secondary_bank;
2894 Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) );
2896 // build up the header portion
2897 BUILD_HEADER( SECONDARY_FIRED_AI );
2899 ADD_DATA( Objects[shipp->objnum].net_signature );
2900 ADD_DATA( starting_sig );
2902 // add a couple of bits for swarm missiles and dual fire secondary weaspons
2905 sinfo = current_bank;
2908 sinfo |= SFPF_ALLOW_SWARM;
2911 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ){
2912 sinfo |= SFPF_DUAL_FIRE;
2915 if ( aip->current_target_is_locked ){
2916 sinfo |= SFPF_TARGET_LOCKED;
2921 // add the ship's target and any targeted subsystem
2922 target_signature = 0;
2924 if ( aip->target_objnum != -1) {
2925 target_signature = Objects[aip->target_objnum].net_signature;
2926 if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
2929 s_index = ship_get_index_from_subsys( aip->targeted_subsys, aip->target_objnum );
2930 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
2931 t_subsys = (char)s_index;
2934 if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
2935 Assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags & WIF_BOMB);
2940 ADD_DATA( target_signature );
2941 ADD_DATA( t_subsys );
2943 // just send this packet to everyone, then bail if an AI ship fired.
2944 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
2945 multi_io_send_to_all(data, packet_size);
2949 net_player_num = multi_find_player_by_object( objp );
2951 // getting here means a player fired. Send the current packet to all players except the player
2952 // who fired. If nothing got fired, then don't send to the other players -- we will just send
2953 // a packet to the player who will find out that he didn't fire anything
2954 if ( num_fired > 0 ) {
2955 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
2958 // if I (the master) fired, then return
2959 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
2963 // now build up the packet to send to the player who actually fired.
2964 BUILD_HEADER( SECONDARY_FIRED_PLR );
2965 ADD_DATA(starting_sig);
2968 // add the targeting information so that the player's weapons will always home on the correct
2970 ADD_DATA( target_signature );
2971 ADD_DATA( t_subsys );
2973 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
2976 /// process a packet indicating a secondary weapon was fired
2977 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
2979 int offset, allow_swarm, target_objnum_save;
2980 ushort net_signature, starting_sig, target_signature;
2981 ubyte sinfo, current_bank;
2982 object* objp, *target_objp;
2986 ship_subsys *targeted_subsys_save;
2988 offset = HEADER_LENGTH; // size of the header
2990 // if from_player is false, it means that the secondary weapon info in this packet was
2991 // fired by an ai object (or another player). from_player == 1 means tha me (the person
2992 // receiving this packet) fired the secondary weapon
2993 if ( !from_player ) {
2994 GET_DATA( net_signature );
2995 GET_DATA( starting_sig );
2996 GET_DATA( sinfo ); // are we firing swarm missiles
2998 GET_DATA( target_signature );
2999 GET_DATA( t_subsys );
3003 // find the object (based on network signatures) for the object that fired
3004 objp = multi_get_network_object( net_signature );
3005 if ( objp == NULL ) {
3006 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3010 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3011 // of the next fire time of this bank to 0 so we can fire right away
3012 shipp = &Ships[objp->instance];
3015 GET_DATA( starting_sig );
3018 GET_DATA( target_signature );
3019 GET_DATA( t_subsys );
3023 // get the object and ship
3025 shipp = Player_ship;
3028 // check the allow swarm bit
3030 if ( sinfo & SFPF_ALLOW_SWARM ){
3034 // set the dual fire properties of the ship
3035 if ( sinfo & SFPF_DUAL_FIRE ){
3036 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3038 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3041 // determine whether current target is locked
3042 Assert( shipp->ai_index != -1 );
3043 aip = &Ai_info[shipp->ai_index];
3044 if ( sinfo & SFPF_TARGET_LOCKED ) {
3045 aip->current_target_is_locked = 1;
3047 aip->current_target_is_locked = 0;
3050 // find out the current bank
3051 current_bank = (ubyte)(sinfo & 0x3);
3052 Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) );
3053 shipp->weapons.current_secondary_bank = current_bank;
3055 // make it so we can fire this ship's secondary bank immediately!!!
3056 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3057 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3059 // set this ship's target and subsystem information. We will save and restore target and
3060 // targeted subsystem so that we do not accidentally change targets for this player or
3061 // any AI ships on his system.
3062 target_objnum_save = aip->target_objnum;
3063 targeted_subsys_save = aip->targeted_subsys;
3065 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3066 aip->target_objnum = -1;
3067 aip->targeted_subsys = NULL;
3069 target_objp = multi_get_network_object( target_signature );
3070 if ( target_objp != NULL ) {
3071 aip->target_objnum = OBJ_INDEX(target_objp);
3073 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3074 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3078 if ( starting_sig != 0 ){
3079 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3081 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3084 ship_fire_secondary( objp, allow_swarm );
3086 // restore targeted object and targeted subsystem
3087 aip->target_objnum = target_objnum_save;
3088 aip->targeted_subsys = targeted_subsys_save;
3091 // send a packet indicating a countermeasure was fired
3092 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3094 ubyte data[MAX_PACKET_SIZE];
3099 Assert ( cmeasure_count < UCHAR_MAX );
3100 BUILD_HEADER(COUNTERMEASURE_FIRED);
3101 ADD_DATA( objp->net_signature );
3102 ADD_DATA( rand_val );
3104 multi_io_send_to_all(data, packet_size);
3107 // process a packet indicating a countermeasure was fired
3108 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3110 int offset, rand_val;
3116 offset = HEADER_LENGTH;
3118 GET_DATA( signature );
3119 GET_DATA( rand_val );
3122 objp = multi_get_network_object( signature );
3123 if ( objp == NULL ) {
3124 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3127 if(objp->type != OBJ_SHIP){
3130 // Assert ( objp->type == OBJ_SHIP );
3132 // make it so ship can fire right away!
3133 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3134 if ( objp == Player_obj ){
3135 nprintf(("network", "firing countermeasure from my ship\n"));
3138 ship_launch_countermeasure( objp, rand_val );
3141 // send a packet indicating that a turret has been fired
3142 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3145 ushort pnet_signature;
3146 ubyte data[MAX_PACKET_SIZE], cindex;
3153 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3157 // local setup -- be sure we are actually passing a weapon!!!!
3158 objp = &Objects[weapon_objnum];
3159 Assert ( objp->type == OBJ_WEAPON );
3160 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3164 pnet_signature = Objects[ship_objnum].net_signature;
3166 Assert( subsys_index < UCHAR_MAX );
3167 cindex = (ubyte)subsys_index;
3169 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3174 // build the fire turret packet.
3175 BUILD_HEADER(FIRE_TURRET_WEAPON);
3176 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.fvec);
3177 ADD_DATA( has_sig );
3178 ADD_DATA( pnet_signature );
3180 ADD_DATA( objp->net_signature );
3183 val = (short)ssp->submodel_info_1.angs.h;
3185 val = (short)ssp->submodel_info_2.angs.p;
3188 multi_io_send_to_all(data, packet_size);
3190 multi_rate_add(1, "tur", packet_size);
3193 // process a packet indicating a turret has been fired
3194 void process_turret_fired_packet( ubyte *data, header *hinfo )
3196 int offset, weapon_objnum, wid;
3197 ushort pnet_signature, wnet_signature;
3206 short pitch, heading;
3208 // get the data for the turret fired packet
3209 offset = HEADER_LENGTH;
3210 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3211 GET_DATA( has_sig );
3212 GET_DATA( pnet_signature );
3214 GET_DATA( wnet_signature );
3218 GET_DATA( turret_index );
3219 GET_DATA( heading );
3221 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3224 objp = multi_get_network_object( pnet_signature );
3225 if ( objp == NULL ) {
3226 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3230 // if this isn't a ship, do nothing
3231 if ( objp->type != OBJ_SHIP ){
3235 // make an orientation matrix from the o_fvec
3236 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3238 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3239 // hack, but should be suitable.
3240 shipp = &Ships[objp->instance];
3241 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3245 wid = ssp->system_info->turret_weapon_type;
3247 // bash the position and orientation of the turret
3248 ssp->submodel_info_1.angs.h = (float)heading;
3249 ssp->submodel_info_2.angs.p = (float)pitch;
3251 // get the world position of the weapon
3252 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3254 // create the weapon object
3255 if(wnet_signature != 0){
3256 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3258 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3259 if (weapon_objnum != -1) {
3260 if ( Weapon_info[wid].launch_snd != -1 ) {
3261 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3266 // send a mission log item packet
3267 void send_mission_log_packet( int num )
3270 ubyte data[MAX_PACKET_SIZE];
3275 Assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3277 // get the data from the log
3278 entry = &log_entries[num];
3279 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3280 sindex = (ushort)entry->index;
3282 BUILD_HEADER(MISSION_LOG_ENTRY);
3284 ADD_DATA(entry->flags);
3286 ADD_DATA(entry->timestamp);
3287 ADD_STRING(entry->pname);
3288 ADD_STRING(entry->sname);
3290 // broadcast the packet to all players
3291 multi_io_send_to_all_reliable(data, packet_size);
3294 // process a mission log item packet
3295 void process_mission_log_packet( ubyte *data, header *hinfo )
3300 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3303 Assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3305 offset = HEADER_LENGTH;
3309 GET_DATA(timestamp);
3315 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3318 // send a mission message packet
3319 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)
3322 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3324 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3325 Assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3326 Assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3328 up = (ubyte) priority;
3329 us = (ubyte) source;
3330 utime = (ubyte)timing;
3332 BUILD_HEADER(MISSION_MESSAGE);
3334 ADD_STRING(who_from);
3338 ADD_DATA(builtin_type);
3339 ADD_DATA(multi_team_filter);
3341 if (multi_target == -1){
3342 multi_io_send_to_all_reliable(data, packet_size);
3344 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3348 // process a mission message packet
3349 void process_mission_message_packet( ubyte *data, header *hinfo )
3351 int offset, id, builtin_type;
3352 ubyte priority, source, utiming;
3353 char who_from[NAME_LENGTH];
3354 int multi_team_filter;
3356 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3358 offset = HEADER_LENGTH;
3360 GET_STRING(who_from);
3364 GET_DATA(builtin_type);
3365 GET_DATA(multi_team_filter);
3369 // filter out builtin ones in TvT
3370 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3374 // maybe filter this out
3375 if(!message_filter_multi(id)){
3376 // send the message as if it came from an sexpression
3377 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3381 // just send them a pong back as fast as possible
3382 void process_ping_packet(ubyte *data, header *hinfo)
3387 offset = HEADER_LENGTH;
3390 // get the address to return the pong to
3391 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3397 // right now it just routes the pong through to the standalone gui, which is the only
3398 // system which uses ping and pong right now.
3399 void process_pong_packet(ubyte *data, header *hinfo)
3405 offset = HEADER_LENGTH;
3407 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3411 // if we're connected , see who sent us this pong
3412 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3413 lookup = find_player_id(hinfo->id);
3418 p = &Net_players[lookup];
3420 // evaluate the ping
3421 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3423 // put in calls to any functions which may want to know about the ping times from
3425 if(Game_mode & GM_STANDALONE_SERVER){
3426 std_update_player_ping(p);
3429 // mark his socket as still alive (extra precaution)
3430 psnet_mark_received(Net_players[lookup].reliable_socket);
3432 // otherwise, do any special processing
3434 // if we're in the join game state, see if this pong came from a server on our
3436 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3437 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3442 // send a ping packet
3443 void send_ping(net_addr *addr)
3445 unsigned char data[8];
3448 // build the header and send the packet
3449 BUILD_HEADER( PING );
3450 psnet_send(addr, &data[0], packet_size);
3453 // send a pong packet
3454 void send_pong(net_addr *addr)
3456 unsigned char data[8];
3459 // build the header and send the packet
3461 psnet_send(addr, &data[0], packet_size);
3464 // sent from host to master. give me the list of missions you have.
3465 // this will be used only in a standalone mode
3466 void send_mission_list_request( int what )
3468 ubyte data[MAX_PACKET_SIZE];
3471 // build the header and ask for a list of missions or campaigns (depending
3472 // on the 'what' flag).
3473 BUILD_HEADER(MISSION_REQUEST);
3475 multi_io_send_reliable(Net_player, data, packet_size);
3478 // maximum number of bytes that we can send in a mission items packet.
3479 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3481 // defines used to tell what type of packets are being sent
3482 #define MISSION_LIST_ITEMS 1
3483 #define CAMPAIGN_LIST_ITEMS 2
3485 // send an individual mission file item
3486 void send_mission_items( net_player *pl )
3488 ubyte data[MAX_PACKET_SIZE];
3493 BUILD_HEADER(MISSION_ITEM);
3495 // send the list of missions and campaigns avilable on the server. Stop when
3496 // reaching a certain maximum
3497 type = MISSION_LIST_ITEMS;
3499 for (i = 0; i < Multi_create_mission_count; i++ ) {
3503 ADD_STRING( Multi_create_mission_list[i].filename );
3504 ADD_STRING( Multi_create_mission_list[i].name );
3505 ADD_DATA( Multi_create_mission_list[i].flags );
3506 ADD_DATA( Multi_create_mission_list[i].max_players );
3507 ADD_DATA( Multi_create_mission_list[i].respawn );
3510 ADD_DATA( Multi_create_mission_list[i].valid_status );
3512 if ( packet_size > MAX_MISSION_ITEMS_BYTES ) {
3515 multi_io_send_reliable(pl, data, packet_size);
3516 BUILD_HEADER( MISSION_ITEM );
3522 multi_io_send_reliable(pl, data, packet_size);
3524 // send the campaign information
3525 type = CAMPAIGN_LIST_ITEMS;
3526 BUILD_HEADER(MISSION_ITEM);
3528 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3532 ADD_STRING( Multi_create_campaign_list[i].filename );
3533 ADD_STRING( Multi_create_campaign_list[i].name );
3534 ADD_DATA( Multi_create_campaign_list[i].flags );
3535 ADD_DATA( Multi_create_campaign_list[i].max_players );
3537 if ( packet_size > MAX_MISSION_ITEMS_BYTES ) {
3540 multi_io_send_reliable(pl, data, packet_size);
3541 BUILD_HEADER( MISSION_ITEM );
3547 multi_io_send_reliable(pl, data, packet_size);
3550 // process a request for a list of missions
3551 void process_mission_request_packet(ubyte *data, header *hinfo)
3553 int player_num,offset;
3555 offset = HEADER_LENGTH;
3558 // fill in the address information of where this came from
3559 player_num = find_player_id(hinfo->id);
3560 if(player_num == -1){
3561 nprintf(("Network","Could not find player to send mission list items to!\n"));
3565 send_mission_items( &Net_players[player_num] );
3568 // process an individual mission file item
3569 void process_mission_item_packet(ubyte *data,header *hinfo)
3572 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3573 ubyte stop, type,max_players;
3576 Assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3577 offset = HEADER_LENGTH;
3582 GET_STRING( filename );
3585 GET_DATA( max_players );
3587 // missions also have respawns and a crc32 associated with them
3588 if(type == MISSION_LIST_ITEMS){
3592 GET_DATA(valid_status);
3594 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3595 strcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename );
3596 strcpy(Multi_create_mission_list[Multi_create_mission_count].name, name );
3597 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3598 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3599 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3602 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3604 Multi_create_mission_count++;
3606 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3607 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3608 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename );
3609 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name );
3610 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3611 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3612 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3613 Multi_create_campaign_count++;
3622 // this will cause whatever list to get resorted (although they should be appearing in order)
3623 multi_create_setup_list_data(-1);
3626 // send a request to the server to pause or unpause the game
3627 void send_multi_pause_packet(int pause)
3629 ubyte data[MAX_PACKET_SIZE];
3631 int packet_size = 0;
3633 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3636 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3637 val = (ubyte) pause;
3639 // add the pause info
3642 // send the request to the server
3643 multi_io_send_reliable(Net_player, data, packet_size);
3646 // process a pause update packet (pause, unpause, etc)
3647 void process_multi_pause_packet(ubyte *data, header *hinfo)
3653 offset = HEADER_LENGTH;
3659 // get who sent the packet
3660 player_index = find_player_id(hinfo->id);
3661 // if we don't know who sent the packet, don't do anything
3662 if(player_index == -1){
3666 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3667 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3670 // send a game information update
3671 void send_game_info_packet()
3674 ubyte data[MAX_PACKET_SIZE], paused;
3676 // set the paused variable
3677 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3679 BUILD_HEADER(GAME_INFO);
3680 ADD_DATA( Missiontime );
3683 multi_io_send_to_all(data, packet_size);
3686 // process a game information update
3687 void process_game_info_packet( ubyte *data, header *hinfo )
3693 offset = HEADER_LENGTH;
3695 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3696 // time, set our time to server time (should take ping time into account!!!)
3697 GET_DATA( mission_time );
3702 // send an ingame nak packet
3703 void send_ingame_nak(int state, net_player *p)
3705 ubyte data[MAX_PACKET_SIZE];
3708 BUILD_HEADER(INGAME_NAK);
3712 multi_io_send_reliable(p, data, packet_size);
3715 // process an ingame nak packet
3716 void process_ingame_nak(ubyte *data, header *hinfo)
3718 int offset,state,pid;
3721 offset = HEADER_LENGTH;
3725 pid = find_player_id(hinfo->id);
3729 pl = &Net_players[pid];
3732 case ACK_FILE_ACCEPTED :
3733 Assert(Net_player->flags & NETINFO_FLAG_INGAME_JOIN);
3734 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3735 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3740 // send a packet telling players to end the mission
3741 void send_endgame_packet(net_player *pl)
3743 ubyte data[MAX_PACKET_SIZE];
3747 BUILD_HEADER(MISSION_END);
3749 // sending to a specific player?
3751 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3752 multi_io_send_reliable(pl, data, packet_size);
3756 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3757 // send all player stats here
3758 multi_broadcast_stats(STATS_MISSION);
3760 // if in dogfight mode, send all dogfight stats as well
3761 ml_string("Before dogfight stats!");
3762 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3763 ml_string("Sending dogfight stats!");
3765 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3767 ml_string("After dogfight stats!");
3769 // tell everyone to leave the game
3770 multi_io_send_to_all_reliable(data, packet_size);
3772 multi_io_send_reliable(Net_player, data, packet_size);
3776 // process a packet indicating we should end the current mission
3777 void process_endgame_packet(ubyte *data, header *hinfo)
3782 offset = HEADER_LENGTH;
3786 ml_string("Receiving endgame packet");
3788 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3789 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3790 // determine who this came from and make sure he is allowed to end the game
3791 player_num = find_player_id(hinfo->id);
3792 Assert(player_num != -1);
3797 // if the player is allowed to end the mission
3798 if(!multi_can_end_mission(&Net_players[player_num])){
3802 // act as if we hit alt+j locally
3803 multi_handle_end_mission_request();
3805 // all clients process immediately
3807 // ingame joiners should quit when they receive an endgame packet since the game is over
3808 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3809 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3813 // do any special processing for being in a state other than the gameplay state
3814 multi_handle_state_special();
3816 // make sure we're not already in the debrief state
3817 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3818 multi_warpout_all_players();
3823 // send a position/orientation update for myself (if I'm an observer)
3824 void send_observer_update_packet()
3826 ubyte data[MAX_PACKET_SIZE];
3831 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3832 // to send any update packets to anyone.
3833 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3837 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3843 BUILD_HEADER(OBSERVER_UPDATE);
3845 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3847 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3850 // add targeting infomation
3851 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3852 target_sig = Objects[Player_ai->target_objnum].net_signature;
3856 ADD_DATA(target_sig);
3858 multi_io_send(Net_player, data, packet_size);
3861 // process a position/orientation update from an observer
3862 void process_observer_update_packet(ubyte *data, header *hinfo)
3868 physics_info bogus_pi;
3871 offset = HEADER_LENGTH;
3873 obs_num = find_player_id(hinfo->id);
3875 memset(&bogus_pi,0,sizeof(physics_info));
3876 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
3878 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
3881 // targeting information
3882 GET_DATA(target_sig);
3885 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
3889 // set targeting info
3890 if(target_sig == 0){
3891 Net_players[obs_num].s_info.target_objnum = -1;
3893 target_obj = multi_get_network_object(target_sig);
3894 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
3897 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
3898 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
3899 Net_players[obs_num].s_info.eye_pos = g_vec;
3900 Net_players[obs_num].s_info.eye_orient = g_mat;
3903 void send_netplayer_slot_packet()
3905 ubyte data[MAX_PACKET_SIZE];
3906 int packet_size,idx;
3911 BUILD_HEADER(NETPLAYER_SLOTS_P);
3912 for(idx=0;idx<MAX_PLAYERS;idx++){
3913 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
3915 ADD_DATA(Net_players[idx].player_id);
3916 ADD_DATA(Objects[Net_players[idx].player->objnum].net_signature);
3917 ADD_DATA(Net_players[idx].p_info.ship_class);
3918 ADD_DATA(Net_players[idx].p_info.ship_index);
3924 // standalone case or not
3925 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3926 multi_io_send_to_all_reliable(data, packet_size);
3928 multi_io_send_reliable(Net_player, data, packet_size);
3932 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
3935 int player_num,ship_class,ship_index;
3941 offset = HEADER_LENGTH;
3943 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
3944 multi_untag_player_ships();
3948 GET_DATA(player_id);
3950 GET_DATA(ship_class);
3951 GET_DATA(ship_index);
3952 player_num = find_player_id(player_id);
3954 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
3956 // call the function in multiutil.cpp to set up the player object stuff
3957 // being careful not to muck with the standalone object
3958 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
3959 objp = multi_get_network_object(net_sig);
3960 Assert(objp != NULL);
3961 multi_assign_player_ship( player_num, objp, ship_class );
3962 Net_players[player_num].p_info.ship_index = ship_index;
3963 objp->flags &= ~(OF_COULD_BE_PLAYER);
3964 objp->flags |= OF_PLAYER_SHIP;
3971 // standalone should forward the packet and wait for a response
3972 if(Game_mode & GM_STANDALONE_SERVER){
3973 send_netplayer_slot_packet();
3976 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
3977 send_netplayer_update_packet();
3980 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
3981 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
3982 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
3983 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
3985 ubyte data[MAX_PACKET_SIZE], utmp;
3988 BUILD_HEADER(SHIP_WSTATE_CHANGE);
3989 ADD_DATA( Objects[shipp->objnum].net_signature );
3990 utmp = (ubyte)(what);
3992 utmp = (ubyte)(new_bank);
3994 utmp = (ubyte)(link_status);
3997 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
3998 multi_io_send_to_all_reliable(data, packet_size);
4001 void process_ship_weapon_change( ubyte *data, header *hinfo )
4005 ubyte what, new_bank, link_status;
4009 offset = HEADER_LENGTH;
4010 GET_DATA( signature );
4012 GET_DATA( new_bank );
4013 GET_DATA( link_status );
4016 objp = multi_get_network_object( signature );
4017 if ( objp == NULL ) {
4018 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4021 // Assert( objp->type == OBJ_SHIP );
4022 if(objp->type != OBJ_SHIP){
4026 // if this is my data, do nothing since I already have my own data
4027 if ( objp == Player_obj ){
4031 // now, get the ship and set the new bank and link modes based on the 'what' value
4032 shipp = &Ships[objp->instance];
4033 if ( what == MULTI_PRIMARY_CHANGED ) {
4034 shipp->weapons.current_primary_bank = new_bank;
4036 shipp->flags |= SF_PRIMARY_LINKED;
4038 shipp->flags &= ~SF_PRIMARY_LINKED;
4041 shipp->weapons.current_secondary_bank = new_bank;
4043 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4045 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4050 // ship status change procedure
4051 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4053 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4054 // for added redundancy.
4055 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4056 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4057 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4058 // back to the client. Also sends multiple times for redundancy
4059 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4060 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4061 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4063 #define SHIP_STATUS_REPEAT 2
4064 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4067 ubyte data[MAX_PACKET_SIZE];
4068 int packet_size = 0;
4074 BUILD_HEADER(SHIP_STATUS_CHANGE);
4076 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4077 temp = bi->status[idx];
4081 // server should send reliably (response packet)
4082 if(MULTIPLAYER_MASTER){
4083 multi_io_send_reliable(pl, data, packet_size);
4085 multi_io_send(pl, data, packet_size);
4089 void process_ship_status_packet(ubyte *data, header *hinfo)
4093 int player_num,unique_id;
4097 offset = HEADER_LENGTH;
4099 // zero out the button info structure for good measure
4100 memset(&bi,0,sizeof(button_info));
4102 // read the button-info
4103 GET_DATA(unique_id);
4105 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4107 bi.status[idx] = i_tmp;
4112 // this will be handled differently client and server side. Duh.
4113 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4114 // find which net-player has sent us butotn information
4115 player_num = find_player_id(hinfo->id);
4116 Assert(player_num >= 0);
4121 // don't process critical button information for observers
4122 // its a new button_info for this guy. apply and ack
4123 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4124 // mark that he's pressed this button
4125 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4127 // send a return packet
4128 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4130 // apply the button presses to his ship as normal
4131 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4133 // else ignore it as a repeat from the same guy
4134 } else { // CLIENT SIDE
4135 // this is the return from the server, so we should now apply them locally
4136 // if(lookup_ship_status(Net_player,unique_id,1)){
4137 multi_apply_ship_status(Net_player, &bi, 1);
4142 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4143 // for clients. Left all details to this function instead of higher level messaging
4145 void send_player_order_packet(int type, int index, int cmd)
4147 ubyte data[MAX_PACKET_SIZE];
4149 ushort target_signature;
4151 int packet_size = 0;
4153 BUILD_HEADER(PLAYER_ORDER_PACKET);
4156 ADD_DATA(val); // ship order or wing order, or message all fighters
4158 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4159 if ( val != SQUAD_MSG_ALL ){
4160 ADD_DATA(index); // net signature of target ship
4163 ADD_DATA(cmd); // the command itself
4166 target_signature = 0;
4167 if ( Player_ai->target_objnum != -1 ){
4168 target_signature = Objects[Player_ai->target_objnum].net_signature;
4171 ADD_DATA( target_signature );
4174 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4177 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4178 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
4179 t_subsys = (char)s_index;
4183 multi_io_send_reliable(Net_player, data, packet_size);
4186 // brief explanation :
4187 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4188 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4189 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4190 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4191 void process_player_order_packet(ubyte *data, header *hinfo)
4193 int offset, player_num, command, index = 0, tobjnum_save;
4194 ushort target_signature;
4195 char t_subsys, type;
4196 object *objp, *target_objp;
4199 ship_subsys *tsubsys_save, *targeted_subsys;
4201 Assert(MULTIPLAYER_MASTER);
4203 // packet values - its easier to read all of these in first
4205 offset = HEADER_LENGTH;
4208 if ( type != SQUAD_MSG_ALL ){
4212 GET_DATA( command );
4213 GET_DATA( target_signature );
4214 GET_DATA( t_subsys );
4218 player_num = find_player_id(hinfo->id);
4219 if(player_num == -1){
4220 nprintf(("Network","Received player order packet from unknown player\n"));
4224 objp = &Objects[Net_players[player_num].player->objnum];
4225 if ( objp->type != OBJ_SHIP ) {
4226 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4230 // HACK HACK HACK HACK HACK HACK
4231 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4232 // the data is BOGUS. All people should be able to to these things as well.
4233 if(command == REARM_REPAIR_ME_ITEM){
4234 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4236 } else if(command == ABORT_REARM_REPAIR_ITEM){
4237 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4241 // if this player is not allowed to do messaging, quit here
4242 if( !multi_can_message(&Net_players[player_num]) ){
4243 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4247 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4248 // then call them in.
4249 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4250 Assert( (index >= 0) && (index < Num_reinforcements) );
4251 hud_squadmsg_call_reinforcement(index, player_num);
4255 // set the player's ai information here
4256 shipp = &Ships[objp->instance];
4257 aip = &Ai_info[shipp->ai_index];
4259 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4260 target_objp = multi_get_network_object( target_signature );
4261 if ( target_objp == NULL ) {
4265 targeted_subsys = NULL;
4266 if ( t_subsys != -1 ) {
4267 Assert( target_objp != NULL );
4268 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4271 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4273 tobjnum_save = aip->target_objnum;
4274 tsubsys_save = aip->targeted_subsys;
4276 if ( target_objp ) {
4277 aip->target_objnum = OBJ_INDEX(target_objp);
4279 aip->target_objnum = -1;
4282 aip->targeted_subsys = targeted_subsys;
4284 if ( type == SQUAD_MSG_SHIP ) {
4285 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4286 } else if ( type == SQUAD_MSG_WING ) {
4287 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4288 } else if ( type == SQUAD_MSG_ALL ) {
4289 hud_squadmsg_send_to_all_fighters( command, player_num );
4292 Assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4293 aip->target_objnum = tobjnum_save;
4294 aip->targeted_subsys = tsubsys_save;
4297 // FILE SIGNATURE stuff :
4298 // there are 2 cases for file signature sending which are handled very differently
4299 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4300 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4301 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4302 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4303 // would have to keep track of up to potentially 14 other ack handles (ouch).
4304 void send_file_sig_packet(ushort sum_sig,int length_sig)
4306 ubyte data[MAX_PACKET_SIZE];
4307 int packet_size = 0;
4309 BUILD_HEADER(FILE_SIG_INFO);
4311 ADD_DATA(length_sig);
4313 multi_io_send_reliable(Net_player, data, packet_size);
4316 void process_file_sig_packet(ubyte *data, header *hinfo)
4321 offset = HEADER_LENGTH;
4323 // should only be received on the server-side
4324 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4327 GET_DATA(length_sig);
4329 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4332 void send_file_sig_request(char *file_name)
4334 ubyte data[MAX_PACKET_SIZE];
4335 int packet_size = 0;
4337 BUILD_HEADER(FILE_SIG_REQUEST);
4338 ADD_STRING(file_name);
4340 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4342 multi_io_send_to_all_reliable(data, packet_size);
4345 void process_file_sig_request(ubyte *data, header *hinfo)
4347 int offset = HEADER_LENGTH;
4349 // get the mission name
4350 GET_STRING(Netgame.mission_name);
4353 // set the current mission filename
4354 strcpy(Game_current_mission_filename,Netgame.mission_name);
4357 multi_get_mission_checksum(Game_current_mission_filename);
4359 if(!multi_endgame_ending()){
4360 // reply to the server
4361 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4365 // functions to deal with subsystems getting whacked
4366 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4368 ubyte data[MAX_PACKET_SIZE];
4371 vector tmp, local_hitpos;
4374 Assert ( index < UCHAR_MAX );
4375 uindex = (ubyte)(index);
4377 objp = &Objects[shipp->objnum];
4379 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4380 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4382 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4383 ADD_DATA( Objects[shipp->objnum].net_signature );
4385 ADD_DATA( local_hitpos );
4387 multi_io_send_to_all_reliable(data, packet_size);
4390 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4396 vector local_hit_pos, world_hit_pos;
4398 offset = HEADER_LENGTH;
4400 GET_DATA( signature );
4402 GET_DATA( local_hit_pos );
4404 // get the network object. process it if we find it.
4405 objp = multi_get_network_object( signature );
4406 if ( objp != NULL ) {
4408 ship_subsys *subsysp;
4410 // be sure we have a ship!!!
4411 // Assert ( objp->type == OBJ_SHIP );
4412 if(objp->type != OBJ_SHIP){
4417 shipp = &Ships[objp->instance];
4419 // call to get the pointer to the subsystem we should be working on
4420 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4421 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4422 vm_vec_add2( &world_hit_pos, &objp->pos );
4424 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4425 if ( objp == Player_obj ) {
4426 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4434 // packet to tell clients cargo of a ship was revealed to all
4435 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4437 ubyte data[MAX_PACKET_SIZE], uindex;
4440 Assert ( index < UCHAR_MAX );
4441 uindex = (ubyte)(index);
4443 // build the header and add the data
4444 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4445 ADD_DATA( Objects[shipp->objnum].net_signature );
4448 // server sends to all players
4449 if(MULTIPLAYER_MASTER){
4450 multi_io_send_to_all_reliable(data, packet_size);
4452 // clients just send to the server
4454 multi_io_send_reliable(Net_player, data, packet_size);
4458 // process a subsystem cargo revealed packet
4459 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4466 ship_subsys *subsysp;
4468 offset = HEADER_LENGTH;
4469 GET_DATA( signature );
4473 // get a ship pointer and call the ship function to reveal the cargo
4474 objp = multi_get_network_object( signature );
4475 if ( objp == NULL ) {
4476 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4480 // Assert( objp->type == OBJ_SHIP );
4481 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4485 shipp = &Ships[objp->instance];
4487 // call to get the pointer to the subsystem we should be working on
4488 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4489 if (subsysp == NULL) {
4490 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4494 // this will take care of re-routing to all other clients
4495 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4496 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4498 // server should rebroadcast
4499 if(MULTIPLAYER_MASTER){
4500 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4504 void send_netplayer_load_packet(net_player *pl)
4506 ubyte data[MAX_PACKET_SIZE];
4507 int packet_size = 0;
4509 BUILD_HEADER(LOAD_MISSION_NOW);
4510 ADD_STRING(Netgame.mission_name);
4513 multi_io_send_to_all_reliable(data, packet_size);
4515 multi_io_send_reliable(pl, data, packet_size);
4519 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4522 int offset = HEADER_LENGTH;
4527 strcpy(Netgame.mission_name,str);
4528 strcpy(Game_current_mission_filename,str);
4529 if(!Multi_mission_loaded){
4531 // MWA 2/3/98 -- ingame join changes!!!
4532 // everyone can go through the same mission loading path here!!!!
4533 nprintf(("Network","Loading mission..."));
4535 // notify everyone that I'm loading the mission
4536 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4537 send_netplayer_update_packet();
4539 // do the load itself
4540 game_start_mission();
4542 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4543 // code will remark the correct player ships
4544 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4545 multi_untag_player_ships();
4548 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4549 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4550 send_netplayer_update_packet();
4552 Multi_mission_loaded = 1;
4553 nprintf(("Network","Finished loading mission\n"));
4557 void send_jump_into_mission_packet(net_player *pl)
4559 ubyte data[MAX_PACKET_SIZE];
4560 int packet_size = 0;
4562 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4564 BUILD_HEADER(JUMP_INTO_GAME);
4566 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4567 // and possible other things.
4569 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4570 ADD_DATA(Netgame.game_state);
4576 multi_io_send_to_all_reliable(data, packet_size);
4578 // send to a specific player
4580 multi_io_send_reliable(pl, data, packet_size);
4584 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4586 int offset = HEADER_LENGTH;
4591 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4592 // the game could be paused, so ingame joiner needs to deal with it.
4593 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4595 Netgame.game_state = state;
4600 // handle any special processing for being in a weird substate
4601 multi_handle_state_special();
4603 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4604 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4605 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4606 multi_ingame_observer_finish();
4608 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4609 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4610 send_netplayer_update_packet();
4613 // start the mission!!
4614 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4615 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4616 gameseq_post_event(GS_EVENT_ENTER_GAME);
4617 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4618 send_netplayer_update_packet();
4622 extern int Player_multi_died_check;
4623 Player_multi_died_check = -1;
4625 // recalc all object pairs now
4626 extern void obj_reset_all_collisions();
4627 obj_reset_all_collisions();
4629 // display some cool text
4630 multi_common_add_text(XSTR("Received mission start\n",720),1);
4633 ml_string(NOX("Client received mission start from server - entering mission"));
4638 char *repair_text[] = {
4640 "REPAIR_INFO_BEGIN",
4642 "REPAIR_INFO_UPDATE",
4643 "REPAIR_INFO_QUEUE",
4644 "REPAIR_INFO_ABORT",
4645 "REPAIR_INFO_BROKEN",
4646 "REPAIR_INFO_WARP_ADD",
4647 "REPAIR_INFO_WARP_REMOVE",
4648 "REPAIR_INFO_ONWAY",
4649 "REPAIR_INFO_KILLED",
4650 "REPAIR_INFO_COMPLETE",
4655 // the following two routines deal with updating and sending information regarding players
4656 // rearming and repairing during the game. The process function calls the routines to deal with
4657 // setting flags and other interesting things.
4658 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4660 int packet_size = 0;
4661 ushort repaired_signature, repair_signature;
4662 ubyte data[MAX_PACKET_SIZE];
4665 // use the network signature of the destination object if there is one, -1 otherwise.
4666 // client will piece it all together
4667 repaired_signature = repaired_objp->net_signature;
4669 // the repair ship may be NULL here since it might have been destroyed
4670 repair_signature = 0;
4672 repair_signature = repair_objp->net_signature;
4675 BUILD_HEADER(CLIENT_REPAIR_INFO);
4678 ADD_DATA( repaired_signature );
4679 ADD_DATA( repair_signature );
4681 multi_io_send_to_all_reliable(data, packet_size);
4683 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));
4686 void process_repair_info_packet(ubyte *data, header *hinfo)
4688 int offset = HEADER_LENGTH;
4689 ushort repaired_signature, repair_signature;
4690 object *repaired_objp, *repair_objp;
4694 GET_DATA( repaired_signature );
4695 GET_DATA( repair_signature );
4698 repaired_objp = multi_get_network_object( repaired_signature );
4699 repair_objp = multi_get_network_object( repair_signature );
4701 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));
4703 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4707 if ( repaired_objp == NULL ) {
4708 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4712 // the hope is to simply call the routine in the ai code to set/unset flags
4713 // based on the code value and everything else should happen..I hope....
4714 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4716 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4718 // set the dock flags when repair begins. Prevents problem in lagging docking
4719 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4721 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4722 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4723 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4726 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4728 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4729 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4732 if ( code == REPAIR_INFO_WARP_ADD ){
4733 mission_warp_in_support_ship( repaired_objp );
4735 mission_remove_scheduled_repair( repaired_objp );
4740 // sends information updating clients on certain AI information that clients will
4741 // need to know about to keep HUD information up to date. objp is the object that we
4742 // are updating, and what is the type of stuff that we are updating.
4743 void send_ai_info_update_packet( object *objp, char what )
4746 ushort other_signature;
4747 ubyte data[MAX_PACKET_SIZE];
4749 ubyte dock_index, dockee_index;
4751 // Assert( objp->type == OBJ_SHIP );
4752 if(objp->type != OBJ_SHIP){
4755 aip = &Ai_info[Ships[objp->instance].ai_index];
4758 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4761 BUILD_HEADER( AI_INFO_UPDATE );
4762 ADD_DATA( objp->net_signature );
4765 // depending on the "what" value, we will send different information
4769 case AI_UPDATE_DOCK:
4770 // for docking ships, add the signature of the ship that we are docking with.
4771 Assert( aip->dock_objnum != -1 );
4772 other_signature = Objects[aip->dock_objnum].net_signature;
4773 dock_index = (ubyte)(aip->dock_index);
4774 dockee_index = (ubyte)(aip->dockee_index);
4775 ADD_DATA( other_signature );
4776 ADD_DATA(dock_index);
4777 ADD_DATA(dockee_index);
4780 case AI_UPDATE_UNDOCK:
4781 // for undocking ships, check the dock_objnum since we might or might not have it
4782 // depending on whether or not a ship was destroyed while we were docked.
4783 other_signature = 0;
4784 if ( aip->dock_objnum != -1 )
4785 other_signature = Objects[aip->dock_objnum].net_signature;
4786 ADD_DATA( other_signature );
4790 case AI_UPDATE_ORDERS: {
4793 // for orders, we only need to send a little bit of information here. Be sure that the
4794 // first order for this ship is active
4795 Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4796 ADD_DATA( aip->goals[0].ai_mode );
4797 ADD_DATA( aip->goals[0].ai_submode );
4799 if ( aip->goals[0].ship_name != NULL )
4800 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4802 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4803 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4804 if ( shipnum != -1 ) {
4805 Assert( Ships[shipnum].objnum != -1 );
4806 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4808 other_signature = 0;
4810 ADD_DATA( other_signature );
4812 // for docking, add the dock and dockee index
4813 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4814 Assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4815 Assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4816 dock_index = (ubyte)aip->goals[0].docker.index;
4817 dockee_index = (ubyte)aip->goals[0].dockee.index;
4818 ADD_DATA( dock_index );
4819 ADD_DATA( dockee_index );
4828 multi_rate_add(1, "aiu", packet_size);
4829 multi_io_send_to_all_reliable(data, packet_size);
4832 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4833 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4834 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4836 int offset = HEADER_LENGTH;
4838 ushort net_signature, other_net_signature;
4839 object *objp, *other_objp;
4842 ubyte dock_index = 0, dockee_index = 0;
4844 GET_DATA( net_signature ); // signature of the object that we are dealing with.
4845 GET_DATA( code ); // code of what we are doing.
4846 objp = multi_get_network_object( net_signature );
4848 nprintf(("Network", "Couldn't find object for ai update\n"));
4851 case AI_UPDATE_DOCK:
4852 GET_DATA( other_net_signature );
4853 GET_DATA( dock_index );
4854 GET_DATA( dockee_index );
4855 other_objp = multi_get_network_object( other_net_signature );
4857 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
4859 // if we don't have an object to work with, break out of loop
4860 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
4864 Assert( other_objp->type == OBJ_SHIP );
4865 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
4866 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
4868 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4869 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4871 ai_do_objects_docked_stuff( objp, other_objp );
4874 case AI_UPDATE_UNDOCK:
4875 GET_DATA( other_net_signature );
4876 other_objp = multi_get_network_object( other_net_signature );
4878 // if we don't have an object to work with, break out of loop
4882 ai_do_objects_undocked_stuff( objp, other_objp );
4885 case AI_UPDATE_ORDERS:
4887 GET_DATA( submode );
4888 GET_DATA( other_net_signature );
4889 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4890 GET_DATA(dock_index);
4891 GET_DATA(dockee_index);
4894 // be sure that we have a ship object!!!
4895 if ( !objp || (objp->type != OBJ_SHIP) )
4898 // set up the information in the first goal element of the object in question
4899 aip = &Ai_info[Ships[objp->instance].ai_index];
4900 aip->active_goal = 0;
4901 aip->goals[0].ai_mode = mode;
4902 aip->goals[0].ai_submode = submode;
4904 // for docking, add the dock and dockee index
4905 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4906 aip->dock_index = dock_index;
4907 aip->dockee_index = dockee_index;
4910 // get a shipname if we can.
4911 other_objp = multi_get_network_object( other_net_signature );
4912 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
4913 // get a pointer to the shipname in question. Use the ship_name value in the
4914 // ship. We are only using this for HUD display, so I think that using this
4915 // method will be fine.
4916 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
4918 // special case for destroy subsystem -- get the ai_info pointer to our target ship
4919 // so that we can properly set up what subsystem this ship is attacking.
4920 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
4921 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
4923 // if docking -- set the dock index and dockee index of this other ship
4924 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4925 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4926 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4933 Int3(); // this Int3() should be temporary
4934 nprintf(("Network", "Invalid code for ai update: %d\n", code));
4940 // tell the standalone to move into the MISSION_SYNC_STATE
4941 void send_mission_sync_packet(int mode,int start_campaign)
4943 ubyte data[MAX_PACKET_SIZE],is_campaign;
4944 int packet_size = 0;
4946 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
4948 // build the header and add the sync mode (pre or post briefing)
4949 BUILD_HEADER(MISSION_SYNC_DATA);
4952 // if this is a campaign game
4953 if(mode == MULTI_SYNC_PRE_BRIEFING){
4954 if(Game_mode & GM_CAMPAIGN_MODE){
4955 // add a byte indicating campaign mode
4957 ADD_DATA(is_campaign);
4959 // add a byte indicating if we should be starting a campaign or continuing it
4960 is_campaign = (ubyte)start_campaign;
4961 ADD_DATA(is_campaign);
4963 // add the campaign filename
4964 ADD_STRING(Netgame.campaign_name);
4966 // otherwise if this is a single mission
4968 // add a byte indicating single mission mode
4970 ADD_DATA(is_campaign);
4972 // add the mission filename
4973 ADD_STRING(Game_current_mission_filename);
4976 multi_io_send_reliable(Net_player, data, packet_size);
4979 // move into the MISSION_SYNC state when this is received
4980 // this packet is sent only from a game host to a standalone
4981 void process_mission_sync_packet(ubyte *data, header *hinfo)
4984 ubyte campaign_flag;
4985 int offset = HEADER_LENGTH;
4987 Assert(Game_mode & GM_STANDALONE_SERVER);
4989 // if this is a team vs team situation, lock the players send a final team update
4990 if(Netgame.type_flags & NG_TYPE_TEAM){
4991 multi_team_host_lock_all();
4992 multi_team_send_update();
4995 // get the sync mode (pre or post briefing)
4998 if(mode == MULTI_SYNC_PRE_BRIEFING){
4999 // get the flag indicating if this is a single mission or a campaign mode
5000 GET_DATA(campaign_flag);
5002 // get the flag indicating whether we should be starting a new campaign
5003 GET_DATA(campaign_flag);
5005 // get the campaign filename
5006 GET_STRING(Netgame.campaign_name);
5008 // either start a new campaign or continue on to the next mission in the current campaign
5010 multi_campaign_start(Netgame.campaign_name);
5012 multi_campaign_next_mission();
5015 // make sure we remove the campaign mode flag
5016 Game_mode &= ~(GM_CAMPAIGN_MODE);
5018 // get the single mission filename
5019 GET_STRING(Game_current_mission_filename);
5020 strcpy(Netgame.mission_name,Game_current_mission_filename);
5025 // set the correct mode and m ove into the state
5026 Multi_sync_mode = mode;
5027 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5030 // tell a player to merge his mission stats into his alltime stats
5031 void send_store_stats_packet(int accept)
5034 int packet_size = 0;
5036 BUILD_HEADER(STORE_MISSION_STATS);
5038 // add whether we're accepting or tossing
5039 val = (ubyte)accept;
5042 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5043 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5044 multi_io_send_to_all_reliable(data, packet_size);
5046 multi_io_send_reliable(Net_player, data, packet_size);
5050 void process_store_stats_packet(ubyte *data, header *hinfo)
5052 int offset = HEADER_LENGTH;
5058 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5059 if(Game_mode & GM_STANDALONE_SERVER){
5060 // rebroadcast the packet to all others in the game
5061 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5062 multi_io_send_to_all_reliable(data, offset);
5065 // all players should mark the stats as being accepted in the debriefing
5066 multi_debrief_stats_accept();
5068 // all players should mark the stats as being "tossed" in the debriefing
5069 multi_debrief_stats_toss();
5074 void send_debris_update_packet(object *objp,int code)
5076 ubyte data[MAX_PACKET_SIZE];
5078 int packet_size = 0;
5080 BUILD_HEADER(DEBRIS_UPDATE);
5081 ADD_DATA(objp->net_signature);
5085 // add any extra relevant data
5087 case DEBRIS_UPDATE_UPDATE:
5088 ADD_DATA(objp->pos); // add position
5089 ADD_ORIENT(objp->orient); // add orientation
5090 ADD_DATA(objp->phys_info.vel); // add velocity
5091 ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5094 multi_io_send_to_all(data, packet_size);
5097 void process_debris_update_packet(ubyte *data, header *hinfo)
5101 object bogus_object;
5103 int offset = HEADER_LENGTH;
5109 objp = multi_get_network_object(net_sig);
5111 objp = &bogus_object;
5115 // update the object
5116 case DEBRIS_UPDATE_UPDATE:
5117 GET_DATA(objp->pos);
5118 GET_ORIENT(objp->orient);
5119 GET_DATA(objp->phys_info.vel);
5120 GET_DATA(objp->phys_info.rotvel);
5122 // simply remove it (no explosion)
5123 case DEBRIS_UPDATE_REMOVE:
5124 if(objp != &bogus_object){
5125 Assert(objp->type == OBJ_DEBRIS);
5126 obj_delete(OBJ_INDEX(objp));
5130 case DEBRIS_UPDATE_NUKE:
5131 if(objp != &bogus_object)
5132 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5140 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)
5142 ubyte data[MAX_PACKET_SIZE];
5144 int packet_size = 0;
5146 BUILD_HEADER(WSS_REQUEST_PACKET);
5148 // add the request information
5149 ADD_DATA(player_id);
5150 ADD_DATA(from_slot);
5151 ADD_DATA(from_index);
5154 ADD_DATA(wl_ship_slot); // only used in weapons loadout
5155 ADD_DATA(ship_class);
5158 // a standard request
5160 multi_io_send_reliable(Net_player, data, packet_size);
5162 // being routed through the standalone to the host of the game
5164 Assert(Game_mode & GM_STANDALONE_SERVER);
5165 multi_io_send_reliable(p, data, packet_size);
5169 void process_wss_request_packet(ubyte *data, header *hinfo)
5171 int offset = HEADER_LENGTH;
5172 int from_slot,from_index;
5173 int to_slot,to_index;
5175 int wl_ship_slot,ship_class;
5179 // determine who this request is from
5180 GET_DATA(player_id);
5181 player_num = find_player_id(player_id);
5183 // read in the request data
5184 GET_DATA(from_slot);
5185 GET_DATA(from_index);
5188 GET_DATA(wl_ship_slot); // only used in weapons loadout
5189 GET_DATA(ship_class); // only used in multi team select
5193 Assert(player_num != -1);
5194 if(player_num == -1){
5198 // if we're the standalone, we have to route this packet to the host of the game
5199 if(Game_mode & GM_STANDALONE_SERVER){
5200 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5202 // otherwise we're the host and should process the request
5205 case WSS_WEAPON_SELECT :
5206 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5208 case WSS_SHIP_SELECT :
5209 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5217 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5219 ubyte data[MAX_PACKET_SIZE],team;
5220 int packet_size = 0;
5222 Assert(size <= (MAX_PACKET_SIZE - 10));
5224 BUILD_HEADER(WSS_UPDATE_PACKET);
5226 // add the team/pool # this is for
5227 team = (ubyte)team_num;
5230 // add the data block size
5233 // add the data itself
5234 memcpy(data + packet_size,wss_data,size);
5235 packet_size += size;
5237 // if we're also the master of the game (not on a standalone)
5238 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5239 multi_io_send_to_all_reliable(data, packet_size);
5241 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5243 multi_io_send_reliable(Net_player, data, packet_size);
5247 void process_wss_update_packet(ubyte *data, header *hinfo)
5250 int size,player_index,idx;
5251 int offset = HEADER_LENGTH;
5253 // get the team/pool #
5256 // get the data size
5259 // if we're the standalone, then we should be routing this data to all the other clients
5260 if(Game_mode & GM_STANDALONE_SERVER){
5265 // determine where this came from
5266 player_index = find_player_id(hinfo->id);
5267 Assert(player_index != -1);
5268 if(player_index < 0){
5272 // route the packet (don't resend it to the host)
5273 for(idx=0;idx<MAX_PLAYERS;idx++){
5274 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5275 multi_io_send_reliable(&Net_players[idx], data, offset);
5279 // set the proper pool pointers
5280 ss_set_team_pointers((int)team);
5282 // read in the block of data, and apply it to the weapons/ship pools
5283 offset += restore_wss_data(data + offset);
5286 // set the pool pointers back to my own team
5287 ss_set_team_pointers(Net_player->p_info.team);
5289 // sync the interface if this was for my own team
5290 if((int)team == Net_player->p_info.team){
5291 multi_ts_sync_interface();
5298 // function to send firing information from the client to the server once they reach
5299 // the final sync screen.
5300 void send_firing_info_packet()
5302 ubyte data[MAX_PACKET_SIZE];
5304 ubyte plinked, sdual;
5306 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5308 BUILD_HEADER(FIRING_INFO);
5309 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5310 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5311 ADD_DATA( plinked );
5314 multi_io_send_reliable(Net_player, data, packet_size);
5317 void process_firing_info_packet( ubyte *data, header *hinfo )
5319 int offset, player_num;
5320 ubyte plinked, sdual;
5323 // only the master of the game should be dealing with these packets
5324 Assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5326 offset = HEADER_LENGTH;
5327 GET_DATA( plinked );
5331 player_num = find_player_id(hinfo->id);
5333 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5337 // get the ship pointer for this player and set the flags accordingly.
5338 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5340 shipp->flags |= SF_PRIMARY_LINKED;
5342 shipp->flags &= ~SF_PRIMARY_LINKED;
5345 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5347 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5350 // packet to deal with changing status of mission goals. used to be sent every so often from server
5351 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5352 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5353 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5354 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5356 ubyte data[MAX_PACKET_SIZE];
5359 BUILD_HEADER(MISSION_GOAL_INFO);
5362 ADD_DATA(new_status);
5365 multi_io_send_to_all_reliable(data, packet_size);
5368 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5370 int offset, goal_num, new_status, valid;
5372 offset = HEADER_LENGTH;
5374 GET_DATA(new_status);
5378 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5379 if ( new_status != -1 ){
5380 mission_goal_status_change( goal_num, new_status );
5382 mission_goal_validation_change( goal_num, valid );
5386 void send_player_settings_packet(net_player *p)
5388 ubyte data[MAX_PACKET_SIZE];
5391 int packet_size = 0;
5394 BUILD_HEADER(PLAYER_SETTINGS);
5396 // add all the data for all the players
5398 for(idx=0;idx<MAX_PLAYERS;idx++){
5399 if(MULTI_CONNECTED(Net_players[idx])){
5401 ADD_DATA(Net_players[idx].player_id);
5403 // break the p_info structure by member, so we don't overwrite any absolute pointers
5404 // ADD_DATA(Net_players[idx].p_info);
5405 ADD_DATA(Net_players[idx].p_info.team);
5406 ADD_DATA(Net_players[idx].p_info.ship_index);
5407 ADD_DATA(Net_players[idx].p_info.ship_class);
5410 // add the stop byte
5414 // either broadcast the data or send to a specific player
5416 multi_io_send_to_all_reliable(data, packet_size);
5418 multi_io_send_reliable(p, data, packet_size);
5422 void process_player_settings_packet(ubyte *data, header *hinfo)
5424 int offset,player_num;
5425 net_player_info bogus,*ptr;
5429 offset = HEADER_LENGTH;
5431 // read in the data for all the players
5433 while(stop != 0xff){
5434 // lookup the player
5435 GET_DATA(player_id);
5436 player_num = find_player_id(player_id);
5438 // make sure this is a valid player
5439 if(player_num == -1){
5442 ptr = &Net_players[player_num].p_info;
5445 GET_DATA(ptr->team);
5446 GET_DATA(ptr->ship_index);
5447 GET_DATA(ptr->ship_class);
5454 // update the server with my new state
5455 // MWA -- 3/31/98 -- check for in mission instead of state.
5456 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5457 if( !(Game_mode & GM_IN_MISSION) ) {
5458 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5459 send_netplayer_update_packet();
5463 // display some cool text
5464 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5467 void send_deny_packet(net_addr *addr, int code)
5470 int packet_size = 0;
5472 // build the header and add the rejection code
5478 psnet_send(addr, data, packet_size);
5481 void process_deny_packet(ubyte *data, header *hinfo)
5485 // get the denial code
5486 offset = HEADER_LENGTH;
5490 // if there is already a dialog active, do nothing - who cares at this point.
5495 // display the appropriate dialog
5497 case JOIN_DENY_JR_STATE :
5498 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));
5500 case JOIN_DENY_JR_TRACKER_INVAL :
5501 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));
5503 case JOIN_DENY_JR_PASSWD :
5504 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5506 case JOIN_DENY_JR_CLOSED :
5507 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));
5509 case JOIN_DENY_JR_TEMP_CLOSED :
5510 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));
5512 case JOIN_DENY_JR_RANK_HIGH :
5513 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));
5515 case JOIN_DENY_JR_RANK_LOW :
5516 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));
5518 case JOIN_DENY_JR_DUP :
5519 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5521 case JOIN_DENY_JR_FULL :
5522 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5524 case JOIN_DENY_JR_BANNED :
5525 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5527 case JOIN_DENY_JR_NOOBS :
5528 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5530 case JOIN_DENY_JR_INGAME_JOIN :
5531 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));
5533 case JOIN_DENY_JR_BAD_VERSION :
5534 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));
5536 case JOIN_DENY_JR_TYPE :
5537 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5541 // call this so that the join request timestamp automatically expires when we hear back from the server
5542 multi_join_reset_join_stamp();
5545 // this packet will consist of
5546 // 1.) netplayer ship classes (85 bytes max)
5547 // 2.) ship weapon state data (241 bytes max)
5548 // 3.) player settings et. al. (133 bytes max)
5549 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5550 void send_post_sync_data_packet(net_player *p, int std_request)
5552 ubyte data[MAX_PACKET_SIZE], val;
5557 ushort sval, ship_ets;
5558 int idx, player_index;
5559 int packet_size = 0;
5563 BUILD_HEADER(POST_SYNC_DATA);
5565 // some header information for standalone packet routing purposes
5566 val = (ubyte)std_request;
5569 // the standalone has two situations
5570 // 1.) sending a request to the host to distribute this block of data
5571 // 2.) having recevied this block of data from the host, it redistributes it
5572 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5573 // case 1, send the request
5574 multi_io_send_reliable(Netgame.host, data, packet_size);
5577 // case 2 for the standalone is below (as normal)
5579 // otherwise build the data now
5581 // add all deleted ships
5582 val = (ubyte)Multi_ts_num_deleted;
5584 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5585 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5591 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5592 shipp = &Ships[Objects[so->objnum].instance];
5594 // don't process non player wing ships
5595 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5601 // # of ships - used multiple times in the packet
5602 val = (ubyte)ship_count;
5605 // add ship class information (85 bytes max)
5606 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5607 shipp = &Ships[Objects[so->objnum].instance];
5609 // don't process non player wing ships
5610 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5613 // add the net signature of the object for look up
5614 ADD_DATA( Objects[so->objnum].net_signature );
5616 // add the ship info index
5617 val = (ubyte)(shipp->ship_info_index);
5620 // add the ships's team select index
5621 val = (ubyte)shipp->ts_index;
5625 // add weapon state information for all starting ships (241 bytes max)
5626 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5627 shipp = &Ships[Objects[so->objnum].instance];
5629 // don't process non player wing ships
5630 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5633 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5635 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5636 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5637 if(player_index == -1){
5640 pl = &Net_players[player_index];
5644 // add the net signature and other weapon information
5645 ADD_DATA( Objects[so->objnum].net_signature );
5647 // add number of primary and secondary banks
5648 bval = (char)(shipp->weapons.num_primary_banks);
5650 bval = (char)(shipp->weapons.num_secondary_banks);
5653 // add weapon bank status
5654 bval = (char)(shipp->weapons.current_primary_bank);
5656 pl->s_info.cur_primary_bank = bval;
5658 // Assert(bval != -1);
5661 bval = (char)(shipp->weapons.current_secondary_bank);
5663 pl->s_info.cur_secondary_bank = bval;
5665 // Assert(bval != -1);
5668 // primary weapon info
5669 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5671 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5674 // secondary weapon info
5675 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5677 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5678 ADD_DATA(val_short);
5679 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5681 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5682 ADD_DATA(val_short);
5683 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5685 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5686 ADD_DATA(val_short);
5688 // send primary and secondary weapon link status
5690 if(shipp->flags & SF_PRIMARY_LINKED){
5692 pl->s_info.cur_link_status |= (1<<0);
5696 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5698 pl->s_info.cur_link_status |= (1<<1);
5702 // if this is a player ship add (1<<2)
5703 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5708 // add a ship ets value
5711 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5713 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5715 ship_ets |= ((ushort)shipp->engine_recharge_index);
5720 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5721 // or if I'm the server as well as the host, I should be sending this to all players
5722 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5723 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5726 multi_io_send_to_all_reliable(data, packet_size);
5728 // send to a specific player
5730 multi_io_send_reliable(p, data, packet_size);
5733 multi_io_send_reliable(Net_player, data, packet_size);
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);
5749 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5751 ubyte val, sinfo_index, ts_index;
5753 ushort net_sig, ship_ets, sval;
5757 int offset = HEADER_LENGTH;
5761 // packet routing information
5764 // 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
5765 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5768 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5769 multi_ts_create_wings();
5771 // send to the standalone through my socket
5772 send_post_sync_data_packet(Net_player);
5778 // add all deleted ships
5780 Multi_ts_num_deleted = (int)val;
5781 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5782 // get the ship's objnum
5785 objp = multi_get_network_object(sval);
5787 // delete the ship appropriately
5788 // mark the object as having been deleted
5789 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5792 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5793 obj_delete(Multi_ts_deleted_objnums[idx]);
5794 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5796 Multi_ts_num_deleted--;
5797 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5805 // process ship class information
5806 for(idx=0; idx<ship_count; idx++){
5807 // get the object's net signature
5809 GET_DATA(sinfo_index);
5812 // attempt to get the object
5813 objp = multi_get_network_object(net_sig);
5815 // make sure we found a ship
5816 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5818 // set the ship to be the right class
5819 change_ship_type(objp->instance,(int)sinfo_index);
5821 // set the ship's team select index
5822 Ships[objp->instance].ts_index = (int)ts_index;
5825 // process ship weapon state info
5826 for(idx=0; idx<ship_count; idx++){
5827 // get the object's net signature
5830 // attempt to get the object
5831 objp = multi_get_network_object(net_sig);
5833 // make sure we found a ship
5834 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5836 // get a pointer to the ship
5837 shipp = &Ships[objp->instance];
5839 // get number of primary and secondary banks;
5842 shipp->weapons.num_primary_banks = (int)b;
5846 shipp->weapons.num_secondary_banks = (int)b;
5848 // get bank selection info
5853 shipp->weapons.current_primary_bank = (int)b;
5859 shipp->weapons.current_secondary_bank = (int)b;
5861 // primary weapon info
5863 shipp->weapons.primary_bank_weapons[0] = (int)b;
5866 shipp->weapons.primary_bank_weapons[1] = (int)b;
5868 // secondary weapon info
5870 shipp->weapons.secondary_bank_weapons[0] = (int)b;
5871 GET_DATA(val_short);
5872 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
5875 shipp->weapons.secondary_bank_weapons[1] = (int)b;
5876 GET_DATA(val_short);
5877 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
5880 shipp->weapons.secondary_bank_weapons[2] = (int)b;
5881 GET_DATA(val_short);
5882 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
5889 shipp->flags |= SF_PRIMARY_LINKED;
5892 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5894 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
5895 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
5897 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
5899 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
5905 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
5907 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
5909 shipp->engine_recharge_index = (ship_ets & 0x000f);
5914 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
5915 send_netplayer_update_packet();
5917 // the standalone server will receive this packet from the host of the game, to be applied locally and
5918 // also to be rebroadcast.
5919 if(Game_mode & GM_STANDALONE_SERVER){
5920 // update player ets settings
5921 for(idx=0;idx<MAX_PLAYERS;idx++){
5922 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
5923 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
5927 send_post_sync_data_packet(NULL,0);
5931 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
5933 ubyte data[MAX_PACKET_SIZE],val;
5936 int packet_size = 0;
5939 BUILD_HEADER(WSS_SLOTS_DATA);
5941 // some header information for standalone packet routing purposes
5942 val = (ubyte)std_request;
5946 val = (ubyte)team_num;
5949 // add whether this is the final packet or not
5953 // the standalone has two situations
5954 // 1.) sending a request to the host to distribute this block of data
5955 // 2.) having recevied this block of data from the host, it redistributes it
5956 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
5957 // case 1, send the request
5958 multi_io_send_reliable(Netgame.host, data, packet_size);
5961 // case 2 for the standalone is below (as normal)
5963 // add all the slots
5964 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
5965 // add the ship class
5966 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
5970 for(i = 0;i<MAX_WL_WEAPONS;i++){
5971 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
5975 // add the weapon counts
5976 for(i = 0;i<MAX_WL_WEAPONS;i++){
5977 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
5978 ADD_DATA(val_short);
5982 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5983 // or if I'm the server as well as the host, I should be sending this to all players
5984 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5985 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5988 multi_io_send_to_all_reliable(data, packet_size);
5990 // send to a specific player
5992 multi_io_send_reliable(p, data, packet_size);
5995 multi_io_send_reliable(Net_player, data, packet_size);
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);
6011 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6013 ubyte val,team_num,final;
6015 int offset = HEADER_LENGTH;
6018 // packet routing information
6024 // get whether this is the final packet or not
6027 // 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
6028 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6031 // send to the standalone through my socket
6032 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6036 // read in all the slot data
6037 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6038 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6040 // get the ship class
6042 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6045 for(i = 0;i<MAX_WL_WEAPONS;i++){
6047 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6049 // check for signed/unsigned problems
6050 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6051 Wss_slots_teams[team_num][idx].wep[i] = -1;
6055 // get the weapon counts
6056 for(i = 0;i<MAX_WL_WEAPONS;i++){
6057 GET_DATA(val_short);
6058 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6063 // update my netplayer state if this is the final packet
6065 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6066 send_netplayer_update_packet();
6069 // the standalone server will receive this packet from the host of the game, to be applied locally and
6070 // also to be rebroadcast.
6071 if(Game_mode & GM_STANDALONE_SERVER){
6072 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6074 // add some mission sync text
6075 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6079 #define OBJ_VISIBILITY_DOT 0.6f
6081 // send and receive packets for shield explosion information
6082 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6085 ubyte data[MAX_PACKET_SIZE], utri_num;
6088 // Assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6090 Assert( tri_num < UCHAR_MAX );
6091 utri_num = (ubyte)tri_num;
6093 // for each player, determine if this object is behind the player -- if so, don't
6095 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6096 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6098 vector eye_to_obj_vec, diff, eye_pos;
6101 eye_pos = Net_players[i].s_info.eye_pos;
6102 eye_orient = Net_players[i].s_info.eye_orient;
6104 // check for same vectors
6105 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6106 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6110 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6111 dot = vm_vec_dot(&eye_orient.fvec, &eye_to_obj_vec);
6113 if ( dot < OBJ_VISIBILITY_DOT ){
6117 BUILD_HEADER(SHIELD_EXPLOSION);
6119 ADD_DATA( Objects[objnum].net_signature );
6122 multi_io_send(&Net_players[i], data, packet_size);
6127 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6129 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6136 // get the shield hit data
6137 offset = HEADER_LENGTH;
6138 GET_DATA(signature);
6140 //GET_DATA(hit_pos);
6143 // find the object with this signature. If found, then do a shield explosion
6144 objp = multi_get_network_object( signature );
6147 shield_info *shieldp;
6152 // given the tri num, find the local position which is the average of the
6153 // three vertices of the triangle affected. Use this average point as the hit
6155 // Assert( objp->type == OBJ_SHIP );
6156 if(objp->type != OBJ_SHIP){
6160 pm = model_get(Ships[objp->instance].modelnum);
6161 shieldp = &pm->shield;
6162 Assert( utri_num < shieldp->ntris );
6163 stri = shieldp->tris[utri_num];
6164 vm_vec_zero(&hit_pos);
6165 for ( i = 0; i < 3; i++ ) {
6166 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6168 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6169 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6173 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6176 ubyte data[MAX_PACKET_SIZE], val;
6178 int packet_size = 0;
6183 sc = &pl->player->stats;
6186 BUILD_HEADER(PLAYER_STATS);
6188 // add the player id
6189 ADD_DATA(pl->player_id);
6191 // add the byte indicating whether these stats are all-time or not
6192 val = (ubyte)stats_code;
6195 // kill information - alltime
6199 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6200 u_tmp = sc->kills[idx];
6203 // medal information
6204 for(idx=0;idx<NUM_MEDALS;idx++){
6205 i_tmp = sc->medals[idx];
6209 ADD_DATA(sc->score);
6211 ADD_DATA(sc->assists);
6212 ADD_DATA(sc->kill_count);
6213 ADD_DATA(sc->kill_count_ok);
6214 ADD_DATA(sc->p_shots_fired);
6215 ADD_DATA(sc->s_shots_fired);
6216 ADD_DATA(sc->p_shots_hit);
6217 ADD_DATA(sc->s_shots_hit);
6218 ADD_DATA(sc->p_bonehead_hits);
6219 ADD_DATA(sc->s_bonehead_hits);
6220 ADD_DATA(sc->bonehead_kills);
6222 ADD_DATA(sc->missions_flown);
6223 ADD_DATA(sc->flight_time);
6224 ADD_DATA(sc->last_flown);
6225 ADD_DATA(sc->last_backup);
6230 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6231 u_tmp = sc->m_okKills[idx];
6235 ADD_DATA(sc->m_score);
6236 ADD_DATA(sc->m_assists);
6237 ADD_DATA(sc->m_kill_count);
6238 ADD_DATA(sc->m_kill_count_ok);
6239 ADD_DATA(sc->mp_shots_fired);
6240 ADD_DATA(sc->ms_shots_fired);
6241 ADD_DATA(sc->mp_shots_hit);
6242 ADD_DATA(sc->ms_shots_hit);
6243 ADD_DATA(sc->mp_bonehead_hits);
6244 ADD_DATA(sc->ms_bonehead_hits);
6245 ADD_DATA(sc->m_bonehead_kills);
6246 ADD_DATA(sc->m_player_deaths);
6247 ADD_DATA(sc->m_medal_earned);
6250 case STATS_MISSION_KILLS:
6251 ADD_DATA(sc->m_kill_count);
6252 ADD_DATA(sc->m_kill_count_ok);
6253 ADD_DATA(sc->m_assists);
6256 case STATS_DOGFIGHT_KILLS:
6257 for(idx=0; idx<MAX_PLAYERS; idx++){
6258 u_tmp = sc->m_dogfight_kills[idx];
6261 ADD_DATA(sc->m_kill_count);
6262 ADD_DATA(sc->m_kill_count_ok);
6263 ADD_DATA(sc->m_assists);
6267 Assert(packet_size < MAX_PACKET_SIZE);
6269 // if we're a client, always send the data to the server
6270 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6271 multi_io_send_reliable(Net_player, data, packet_size);
6273 // otherwise do server specific stuff
6275 // send to a specific target
6277 multi_io_send_reliable(target, data, packet_size);
6279 // otherwise, send to everyone
6281 multi_io_send_to_all_reliable(data, packet_size);
6286 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6290 scoring_struct *sc,bogus;
6292 int offset = HEADER_LENGTH;
6296 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6298 // get the player who these stats are for
6299 GET_DATA(player_id);
6300 player_num = find_player_id(player_id);
6301 if (player_num == -1) {
6302 nprintf(("Network", "Couldn't find player for stats update!\n"));
6303 ml_string("Couldn't find player for stats update!\n");
6308 sc = &Net_players[player_num].player->stats;
6311 // get the stats code
6315 ml_string("Received STATS_ALLTIME\n");
6318 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6320 sc->kills[idx] = u_tmp;
6323 // read in the stats
6324 for (idx=0; idx<NUM_MEDALS; idx++) {
6326 sc->medals[idx] = i_tmp;
6329 GET_DATA(sc->score);
6331 GET_DATA(sc->assists);
6332 GET_DATA(sc->kill_count);
6333 GET_DATA(sc->kill_count_ok);
6334 GET_DATA(sc->p_shots_fired);
6335 GET_DATA(sc->s_shots_fired);
6336 GET_DATA(sc->p_shots_hit);
6337 GET_DATA(sc->s_shots_hit);
6338 GET_DATA(sc->p_bonehead_hits);
6339 GET_DATA(sc->s_bonehead_hits);
6340 GET_DATA(sc->bonehead_kills);
6342 GET_DATA(sc->missions_flown);
6343 GET_DATA(sc->flight_time);
6344 GET_DATA(sc->last_flown);
6345 GET_DATA(sc->last_backup);
6349 ml_string("Received STATS_MISSION\n");
6351 // kills - mission OK
6352 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6354 sc->m_okKills[idx] = u_tmp;
6357 GET_DATA(sc->m_score);
6358 GET_DATA(sc->m_assists);
6359 GET_DATA(sc->m_kill_count);
6360 GET_DATA(sc->m_kill_count_ok);
6361 GET_DATA(sc->mp_shots_fired);
6362 GET_DATA(sc->ms_shots_fired);
6363 GET_DATA(sc->mp_shots_hit);
6364 GET_DATA(sc->ms_shots_hit);
6365 GET_DATA(sc->mp_bonehead_hits);
6366 GET_DATA(sc->ms_bonehead_hits);
6367 GET_DATA(sc->m_bonehead_kills);
6368 GET_DATA(sc->m_player_deaths);
6369 GET_DATA(sc->m_medal_earned);
6372 case STATS_MISSION_KILLS:
6373 ml_string("Received STATS_MISSION_KILLS\n");
6375 GET_DATA(sc->m_kill_count);
6376 GET_DATA(sc->m_kill_count_ok);
6377 GET_DATA(sc->m_assists);
6380 case STATS_DOGFIGHT_KILLS:
6381 ml_string("Received STATS_DOGFIGHT_KILLS\n");
6382 if(player_num >= 0){
6383 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6385 for(idx=0; idx<MAX_PLAYERS; idx++){
6387 sc->m_dogfight_kills[idx] = u_tmp;
6388 if(player_num >= 0){
6389 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6392 GET_DATA(sc->m_kill_count);
6393 GET_DATA(sc->m_kill_count_ok);
6394 GET_DATA(sc->m_assists);
6399 // if I'm the server of the game, I should always rebroadcast these stats
6400 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6401 // make sure these are alltime stats
6402 Assert(val == STATS_ALLTIME);
6404 multi_broadcast_stats(STATS_ALLTIME);
6408 // called to create asteroid stuff
6409 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6412 ubyte data[MAX_PACKET_SIZE];
6413 ubyte packet_type, atype;
6417 if (relvec != NULL )
6420 BUILD_HEADER( ASTEROID_INFO );
6421 packet_type = ASTEROID_CREATE;
6423 Assert( asteroid_type < UCHAR_MAX );
6424 atype = (ubyte)asteroid_type;
6426 ADD_DATA( packet_type );
6427 ADD_DATA( parent_objp->net_signature );
6428 ADD_DATA( new_objp->net_signature );
6432 multi_io_send_to_all(data, packet_size);
6435 void send_asteroid_throw( object *objp )
6438 ubyte data[MAX_PACKET_SIZE], packet_type;
6440 BUILD_HEADER( ASTEROID_INFO );
6442 // this packet type is an asteroid throw
6443 packet_type = ASTEROID_THROW;
6444 ADD_DATA( packet_type );
6445 ADD_DATA( objp->net_signature );
6446 ADD_DATA( objp->pos );
6447 ADD_DATA( objp->phys_info.vel );
6449 multi_io_send_to_all(data, packet_size);
6452 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6455 ubyte data[MAX_PACKET_SIZE], packet_type;
6459 if ( hitpos != NULL )
6462 // build up an asteroid hit packet
6463 BUILD_HEADER( ASTEROID_INFO );
6464 packet_type = ASTEROID_HIT;
6465 ADD_DATA( packet_type );
6466 ADD_DATA( objp->net_signature );
6468 if(other_objp == NULL){
6469 ushort invalid_sig = 0xffff;
6470 ADD_DATA(invalid_sig);
6472 ADD_DATA( other_objp->net_signature );
6477 multi_io_send_to_all(data, packet_size);
6480 void process_asteroid_info( ubyte *data, header *hinfo )
6485 offset = HEADER_LENGTH;
6486 GET_DATA( packet_type );
6488 // based on the packet type, do something interesting with an asteroid!
6489 switch( packet_type ) {
6491 case ASTEROID_CREATE: {
6492 ushort psignature, signature;
6495 object *parent_objp;
6497 GET_DATA( psignature );
6498 GET_DATA( signature );
6502 // after getting the values, set the next network signature, and call the create sub function
6503 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6504 parent_objp = multi_get_network_object( psignature );
6505 if ( parent_objp ) {
6506 asteroid_sub_create( parent_objp, atype, &relvec );
6508 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6515 // asteroid throw packet -- asteroid has wrapped bounds
6516 case ASTEROID_THROW: {
6521 GET_DATA( signature );
6524 objp = multi_get_network_object( signature );
6526 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6530 objp->phys_info.vel = vel;
6531 objp->phys_info.desired_vel = vel;
6535 case ASTEROID_HIT: {
6536 ushort signature, osignature;
6537 object *objp, *other_objp;
6541 GET_DATA( signature );
6542 GET_DATA( osignature );
6546 objp = multi_get_network_object( signature );
6547 if(osignature == 0xffff){
6550 other_objp = multi_get_network_object( osignature );
6553 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6557 if ( IS_VEC_NULL(&hitpos) ){
6558 asteroid_hit( objp, other_objp, NULL, damage );
6560 asteroid_hit( objp, other_objp, &hitpos, damage );
6563 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6564 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6565 weapon_hit( other_objp, objp, &hitpos );
6578 void send_host_restr_packet(char *callsign,int code,int mode)
6580 ubyte data[MAX_PACKET_SIZE],val;
6581 int packet_size = 0;
6583 // build the header and add the opcode
6584 BUILD_HEADER(HOST_RESTR_QUERY);
6591 ADD_STRING(callsign);
6593 // if I'm the standalone server, I should be sending this to the game host
6594 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6595 multi_io_send_reliable(Netgame.host, data, packet_size);
6597 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6599 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6600 multi_io_send_reliable(Net_player, data, packet_size);
6604 void process_host_restr_packet(ubyte *data, header *hinfo)
6608 int offset = HEADER_LENGTH;
6610 // get the opcode and the callsign
6613 GET_STRING(callsign);
6616 // do code specific operations
6618 // query to the host from standalone
6620 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6622 // set the join mode
6623 Multi_join_restr_mode = mode;
6625 // set the timestamp
6626 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6628 // notify the host of the event
6629 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6630 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6633 // affirmative reply from the host to the standalone
6635 Assert(Game_mode & GM_STANDALONE_SERVER);
6637 // let the player join if the timestamp has not already elapsed on the server
6638 if(Multi_restr_query_timestamp != -1){
6639 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6645 Assert(Game_mode & GM_STANDALONE_SERVER);
6646 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6647 Multi_restr_query_timestamp = -1;
6652 void send_netgame_end_error_packet(int notify_code,int err_code)
6656 int packet_size = 0;
6658 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6659 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6661 // build the header and add the notification and error codes
6662 BUILD_HEADER(NETGAME_END_ERROR);
6663 code = (char)notify_code;
6665 code = (char)err_code;
6669 multi_io_send_to_all_reliable(data, packet_size);
6672 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6674 int offset = HEADER_LENGTH;
6675 char notify_code,error_code;
6677 // get the error and notification codes
6678 GET_DATA(notify_code);
6679 GET_DATA(error_code);
6683 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6686 // sends info that a countermeasure succeeded.
6687 void send_countermeasure_success_packet( int objnum )
6689 int pnum, packet_size;
6690 ubyte data[MAX_PACKET_SIZE];
6692 pnum = multi_find_player_by_object( &Objects[objnum] );
6694 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6698 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6699 multi_io_send(&Net_players[pnum], data, packet_size);
6702 // start the flashing of my hud gauge
6703 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6707 offset = HEADER_LENGTH;
6710 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6711 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6714 #define UPDATE_IS_PAUSED (1<<0)
6715 #define UPDATE_HULL_INFO (1<<1)
6717 void send_client_update_packet(net_player *pl)
6719 ubyte data[MAX_PACKET_SIZE],val;
6720 int packet_size = 0;
6723 BUILD_HEADER(CLIENT_UPDATE);
6727 // add the pause status
6728 if ( Multi_pause_status ) {
6729 val |= UPDATE_IS_PAUSED;
6730 } 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) ) {
6731 val |= UPDATE_HULL_INFO;
6732 Assert( Player_ship ); // I"d better have one of these!!!!
6737 // if paused, add the net address of the guy who paused
6738 if(val & UPDATE_IS_PAUSED){
6739 Assert(Multi_pause_pauser != NULL);
6740 ADD_DATA(Multi_pause_pauser->player_id);
6743 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6744 if ( val & UPDATE_HULL_INFO ) {
6746 ubyte percent, ns, threats;
6749 ship_subsys *subsysp;
6752 // get the object for the player
6753 Assert( pl->player->objnum != -1 );
6755 objp = &Objects[pl->player->objnum];
6757 Assert ( objp->type == OBJ_SHIP );
6758 shipp = &Ships[objp->instance];
6759 sip = &Ship_info[shipp->ship_info_index];
6761 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6762 // percentage value since that should be close enough
6763 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6767 percent = (ubyte)temp;
6769 ADD_DATA( percent );
6771 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6772 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6773 ADD_DATA( percent );
6776 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6777 // both machines. Added as percent since that number <= 100
6779 // also write out the number of subsystems. We do this because the client might not know
6780 // about the object he is getting data for. (i.e. he killed the object already).
6781 ns = (ubyte)sip->n_subsystems;
6784 // now the subsystems.
6785 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6786 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6787 ADD_DATA( percent );
6790 // compute the threats for this player. Only compute the threats if this player is actually
6791 // playing (i.e. he has a ship)
6792 hud_update_reticle( pl->player );
6793 threats = (ubyte)pl->player->threat_flags;
6794 ADD_DATA( threats );
6796 // add his energy level for guns
6797 ADD_DATA(shipp->weapon_energy);
6799 // add his secondary bank ammo
6800 ADD_DATA(shipp->weapons.num_secondary_banks);
6801 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6802 ADD_DATA(shipp->weapons.secondary_bank_ammo[i]);
6807 ADD_DATA(pl->sv_last_pl);
6809 // send the packet reliably to the player
6810 multi_io_send(pl, data, packet_size);
6813 void process_client_update_packet(ubyte *data, header *hinfo)
6818 int is_paused, have_hull_info;
6821 float weapon_energy;
6822 int offset = HEADER_LENGTH;
6824 // get the header byte containing useful information
6827 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6828 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6830 // if we are paused, get who paused
6833 player_index = find_player_id(pauser);
6834 if(player_index != -1){
6835 Multi_pause_pauser = &Net_players[player_index];
6837 Multi_pause_pauser = NULL;
6841 // if we have hull information, then read it in.
6842 if ( have_hull_info ) {
6846 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
6848 ship_subsys *subsysp;
6852 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6853 // percentage value since that should be close enough
6854 GET_DATA( hull_percent );
6856 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
6858 shield_percent[i] = ub_tmp;
6861 // get the data for the subsystems
6862 GET_DATA( n_subsystems );
6863 for ( i = 0; i < n_subsystems; i++ ){
6865 subsystem_percent[i] = ub_tmp;
6868 GET_DATA( threats );
6870 // add his energy level for guns
6871 GET_DATA(weapon_energy);
6873 // add his secondary bank ammo
6874 GET_DATA(ammo_count);
6875 for(i=0; i<ammo_count; i++){
6879 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
6880 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
6881 // UDP client update packets. Only read this info if I have a ship.
6882 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
6883 shipp = Player_ship;
6885 sip = &Ship_info[shipp->ship_info_index];
6887 val = hull_percent * sip->initial_hull_strength / 100.0f;
6888 objp->hull_strength = val;
6890 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6891 val = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
6892 objp->shields[i] = val;
6895 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
6896 // then don't read these in.
6897 if ( n_subsystems == sip->n_subsystems ) {
6899 n_subsystems = 0; // reuse this variable
6900 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6903 val = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
6904 subsysp->current_hits = val;
6906 // add the value just generated (it was zero'ed above) into the array of generic system types
6907 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
6908 Assert ( subsys_type < SUBSYSTEM_MAX );
6909 shipp->subsys_info[subsys_type].current_hits += val;
6913 ship_recalc_subsys_strength( shipp );
6915 shipp->weapon_energy = weapon_energy;
6916 for(i=0; i<ammo_count; i++){
6917 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
6920 // update my threat flags.
6921 // temporarily commented out until tested.
6922 Net_player->player->threat_flags = threats;
6929 if(Net_player != NULL){
6930 Net_player->cl_last_pl = pl;
6934 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
6935 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
6937 multi_pause_pause();
6939 multi_pause_unpause();
6944 void send_countdown_packet(int time)
6948 int packet_size = 0;
6950 // build the header and add the time
6951 BUILD_HEADER(COUNTDOWN);
6955 // if we're the server, we should broadcast to everyone
6956 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6957 multi_io_send_to_all_reliable(data, packet_size);
6959 // otherwise we'de better be a host sending to the standalone
6961 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6962 multi_io_send_reliable(Net_player, data, packet_size);
6966 void process_countdown_packet(ubyte *data, header *hinfo)
6968 int offset = HEADER_LENGTH;
6975 // if we're not in the post sync data screen, ignore it
6976 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
6980 // if we're the standalone, this should be a -1 telling us to start the countdown
6981 if(Game_mode & GM_STANDALONE_SERVER){
6982 Assert((int)time == -1);
6984 // start the countdown
6985 multi_sync_start_countdown();
6987 // otherwise if we're clients, just bash the countdown
6989 Multi_sync_countdown = (int)time;
6993 // packets for debriefing information
6994 void send_debrief_info( int stage_count[], int *stages[] )
6996 ubyte data[MAX_PACKET_SIZE];
6997 int packet_size, i, j;
7000 BUILD_HEADER(DEBRIEF_INFO);
7002 // add the data for the teams
7003 for ( i = 0; i < Num_teams; i++ ) {
7006 count = stage_count[i];
7008 for ( j = 0; j < count; j++ ) {
7009 i_tmp = stages[i][j];
7014 multi_io_send_to_all_reliable(data, packet_size);
7017 // process a debrief info packet from the server
7018 void process_debrief_info( ubyte *data, header *hinfo )
7021 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7024 offset = HEADER_LENGTH;
7025 for ( i = 0; i < Num_teams; i++ ) {
7029 stage_counts[i] = count;
7030 stages[i] = active_stages[i];
7031 for ( j = 0; j < count; j++ ) {
7033 active_stages[i][j] = i_tmp;
7038 // now that we have the stage data for the debriefing stages, call debrief function with the
7039 // data so that clients can now see the debriefing stuff. Do it only for my team.
7040 Assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7041 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7044 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7045 // sends homing_object and homing_subsystem to all clients.
7046 void send_homing_weapon_info( int weapon_num )
7048 ubyte data[MAX_PACKET_SIZE];
7051 object *homing_object;
7052 ushort homing_signature;
7055 wp = &Weapons[weapon_num];
7057 // be sure that this weapon object is a homing object.
7058 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7061 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7062 // homing signature.
7063 homing_signature = 0;
7064 homing_object = wp->homing_object;
7065 if ( homing_object != NULL ) {
7066 homing_signature = homing_object->net_signature;
7068 // get the subsystem index.
7070 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7073 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7074 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
7075 t_subsys = (char)s_index;
7079 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7080 ADD_DATA( Objects[wp->objnum].net_signature );
7081 ADD_DATA( homing_signature );
7082 ADD_DATA( t_subsys );
7084 multi_io_send_to_all(data, packet_size);
7087 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7088 // packet contains information for multiple weapons (like hornets).
7089 void process_homing_weapon_info( ubyte *data, header *hinfo )
7092 ushort weapon_signature, homing_signature;
7094 object *homing_object, *weapon_objp;
7097 offset = HEADER_LENGTH;
7099 // get the data for the packet
7100 GET_DATA( weapon_signature );
7101 GET_DATA( homing_signature );
7102 GET_DATA( h_subsys );
7105 // deal with changing this weapons homing information
7106 weapon_objp = multi_get_network_object( weapon_signature );
7107 if ( weapon_objp == NULL ) {
7108 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7111 Assert( weapon_objp->type == OBJ_WEAPON );
7112 wp = &Weapons[weapon_objp->instance];
7114 // be sure that we can find these weapons and
7115 homing_object = multi_get_network_object( homing_signature );
7116 if ( homing_object == NULL ) {
7117 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7121 if ( homing_object->type == OBJ_WEAPON ) {
7122 Assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7125 wp->homing_object = homing_object;
7126 wp->homing_subsys = NULL;
7127 wp->target_num = OBJ_INDEX(homing_object);
7128 wp->target_sig = homing_object->signature;
7129 if ( h_subsys != -1 ) {
7130 Assert( homing_object->type == OBJ_SHIP );
7131 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7134 if ( homing_object->type == OBJ_SHIP ) {
7135 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7139 void send_emp_effect(ushort net_sig, float intensity, float time)
7144 Assert(MULTIPLAYER_MASTER);
7146 // build the packet and add the opcode
7147 BUILD_HEADER(EMP_EFFECT);
7149 ADD_DATA(intensity);
7152 // send it to the player
7153 multi_io_send_to_all(data, packet_size);
7156 void process_emp_effect(ubyte *data, header *hinfo)
7158 float intensity, time;
7161 int offset = HEADER_LENGTH;
7163 // read in the EMP effect data
7165 GET_DATA(intensity);
7169 // try and find the object
7170 objp = multi_get_network_object(net_sig);
7171 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7172 // if i'm not an observer and I have a valid ship, play the EMP effect
7173 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7174 emp_start_local(intensity, time);
7177 // start the effect for the ship itself
7178 emp_start_ship(objp, intensity, time);
7182 // tells whether or not reinforcements are available
7183 void send_reinforcement_avail( int rnum )
7188 BUILD_HEADER(REINFORCEMENT_AVAIL);
7190 multi_io_send_to_all_reliable(data, packet_size);
7193 void process_reinforcement_avail( ubyte *data, header *hinfo )
7198 offset = HEADER_LENGTH;
7202 // sanity check for a valid reinforcement number
7203 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7204 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7208 void send_change_iff_packet(ushort net_signature, int new_team)
7210 ubyte data[MAX_PACKET_SIZE];
7211 int packet_size = 0;
7213 if(Net_player == NULL){
7216 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7220 // build the packet and add the data
7221 BUILD_HEADER(CHANGE_IFF);
7222 ADD_DATA(net_signature);
7225 // send to all players
7226 multi_io_send_to_all_reliable(data, packet_size);
7229 void process_change_iff_packet( ubyte *data, header *hinfo)
7231 int offset = HEADER_LENGTH;
7232 ushort net_signature;
7237 GET_DATA(net_signature);
7241 // lookup the object
7242 objp = multi_get_network_object(net_signature);
7243 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7244 Ships[objp->instance].team = new_team;
7248 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7250 int packet_size, objnum;
7251 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7254 net_player *ignore = NULL;
7256 // sanity checking for now
7257 Assert ( banks_fired <= 3 );
7259 // get an object pointer for this ship.
7260 objnum = shipp->objnum;
7261 objp = &Objects[objnum];
7263 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7264 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7268 // just in case nothing got fired
7269 if(banks_fired <= 0){
7273 // ubanks_fired = (ubyte)banks_fired;
7274 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7275 // Assert( current_bank <= 3 );
7277 // insert the current primary bank into this byte
7278 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7280 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7281 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7282 // ubanks_fired |= (1<<7);
7285 // determine if its a player ship and don't send to him if we're in "client firing" mode
7286 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7287 if(MULTIPLAYER_MASTER){
7288 np_index = multi_find_player_by_net_signature(objp->net_signature);
7289 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7290 ignore = &Net_players[np_index];
7294 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7295 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7296 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7297 // and server in sync w.r.t. weapon energy for player ship
7298 BUILD_HEADER( PRIMARY_FIRED_NEW );
7299 ADD_DATA(objp->net_signature);
7300 // ADD_DATA(ubanks_fired);
7302 // if I'm a server, broadcast to all players
7303 if(MULTIPLAYER_MASTER){
7304 multi_io_send_to_all(data, packet_size, ignore);
7307 multi_rate_add(1, "wfi", packet_size);
7309 // otherwise just send to the server
7311 multi_io_send(Net_player, data, packet_size);
7315 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7317 int offset; // linked;
7318 // ubyte banks_fired, current_bank;
7323 // read all packet info
7324 offset = HEADER_LENGTH;
7325 GET_DATA(shooter_sig);
7326 // GET_DATA(banks_fired);
7329 // find the object this fired packet is operating on
7330 objp = multi_get_network_object( shooter_sig );
7331 if ( objp == NULL ) {
7332 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7335 // if this object is not actually a valid ship, don't do anything
7336 if(objp->type != OBJ_SHIP){
7339 if(objp->instance < 0){
7342 shipp = &Ships[objp->instance];
7344 // get the link status of the primary banks
7346 // if ( banks_fired & (1<<7) ) {
7348 // banks_fired ^= (1<<7);
7351 // get the current primary bank
7352 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7353 // current_bank &= 0x3;
7354 // Assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7355 // shipp->weapons.current_primary_bank = current_bank;
7357 // strip off all remaining bits and just keep which banks were actually fired.
7358 // banks_fired &= 0x3;
7360 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7363 // shipp->flags &= ~SF_PRIMARY_LINKED;
7365 // shipp->flags |= SF_PRIMARY_LINKED;
7368 // if we're in client firing mode, ignore ones for myself
7369 if((Player_obj != NULL) && (Player_obj == objp)){
7373 ship_fire_primary( objp, 0, 1 );
7376 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7378 ubyte data[MAX_PACKET_SIZE];
7381 net_player *ignore = NULL;
7383 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7384 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7388 Assert ( cmeasure_count < UCHAR_MAX );
7389 BUILD_HEADER(COUNTERMEASURE_NEW);
7390 ADD_DATA( objp->net_signature );
7391 ADD_DATA( rand_val );
7393 nprintf(("Network","Sending NEW countermeasure packet!\n"));
7395 // determine if its a player ship and don't send to him if we're in "client firing" mode
7396 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7397 if(MULTIPLAYER_MASTER){
7398 np_index = multi_find_player_by_net_signature(objp->net_signature);
7399 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7400 ignore = &Net_players[np_index];
7404 // if I'm the server, send to all players
7405 if(MULTIPLAYER_MASTER){
7406 multi_io_send_to_all(data, packet_size, ignore);
7408 // otherwise send to the server
7410 multi_io_send(Net_player, data, packet_size);
7414 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7421 offset = HEADER_LENGTH;
7422 GET_DATA( signature );
7423 GET_DATA( rand_val );
7426 objp = multi_get_network_object( signature );
7427 if ( objp == NULL ) {
7428 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7431 if(objp->type != OBJ_SHIP){
7435 // if we're in client firing mode, ignore ones for myself
7436 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7437 if((Player_obj != NULL) && (Player_obj == objp)){
7441 // make it so ship can fire right away!
7442 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7443 if ( objp == Player_obj ){
7444 nprintf(("network", "firing countermeasure from my ship\n"));
7446 ship_launch_countermeasure( objp, rand_val );
7449 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7451 ubyte data[MAX_PACKET_SIZE];
7452 int packet_size = 0;
7456 // only the server should ever be doing this
7457 Assert(MULTIPLAYER_MASTER);
7459 // setup outgoing data
7460 Assert(shooter != NULL);
7461 Assert(turret != NULL);
7462 Assert(target != NULL);
7463 Assert(override != NULL);
7464 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7467 u_beam_info = (ubyte)beam_info_index;
7468 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7469 Assert(subsys_index >= 0);
7470 if(subsys_index < 0){
7475 BUILD_HEADER(BEAM_FIRED);
7476 ADD_DATA(shooter->net_signature);
7477 ADD_DATA(subsys_index);
7478 ADD_DATA(target->net_signature);
7479 ADD_DATA(u_beam_info);
7480 ADD_DATA((*override));
7482 // send to all clients
7483 multi_io_send_to_all_reliable(data, packet_size);
7486 void process_beam_fired_packet(ubyte *data, header *hinfo)
7489 ushort shooter_sig, target_sig;
7493 beam_fire_info fire_info;
7495 // only clients should ever get this
7496 Assert(MULTIPLAYER_CLIENT);
7498 // read in packet data
7499 offset = HEADER_LENGTH;
7500 GET_DATA(shooter_sig);
7501 GET_DATA(subsys_index);
7502 GET_DATA(target_sig);
7503 GET_DATA(u_beam_info);
7507 // lookup all relevant data
7508 fire_info.beam_info_index = (int)u_beam_info;
7509 fire_info.shooter = NULL;
7510 fire_info.target = NULL;
7511 fire_info.turret = NULL;
7512 fire_info.target_subsys = NULL;
7513 fire_info.beam_info_override = NULL;
7514 fire_info.shooter = multi_get_network_object(shooter_sig);
7515 fire_info.target = multi_get_network_object(target_sig);
7516 fire_info.beam_info_override = &b_info;
7517 fire_info.accuracy = 1.0f;
7518 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)){
7519 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7522 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7523 if(fire_info.turret == NULL){
7524 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7529 beam_fire(&fire_info);
7532 void send_sw_query_packet(ubyte code, char *txt)
7534 ubyte data[MAX_PACKET_SIZE];
7535 int packet_size = 0;
7537 // build the packet and add the code
7538 BUILD_HEADER(SW_STD_QUERY);
7540 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7544 // if I'm the host, send to standalone
7545 if(MULTIPLAYER_HOST){
7546 Assert(!MULTIPLAYER_MASTER);
7547 Assert(code == SW_STD_START);
7548 multi_io_send_reliable(Net_player, data, packet_size);
7550 // otherwise standalone sends back to host
7552 Assert(Game_mode & GM_STANDALONE_SERVER);
7553 Assert(code != SW_STD_START);
7554 Assert(Netgame.host != NULL);
7555 if(Netgame.host != NULL){
7556 multi_io_send_reliable(Netgame.host, data, packet_size);
7561 void process_sw_query_packet(ubyte *data, header *hinfo)
7565 void send_event_update_packet(int event)
7567 ubyte data[MAX_PACKET_SIZE];
7568 ushort u_event = (ushort)event;
7569 int packet_size = 0;
7571 // build the header and add the event
7572 BUILD_HEADER(EVENT_UPDATE);
7574 ADD_DATA(Mission_events[event].flags);
7575 ADD_DATA(Mission_events[event].formula);
7576 ADD_DATA(Mission_events[event].result);
7577 ADD_DATA(Mission_events[event].count);
7579 // send to all players
7580 multi_io_send_to_all_reliable(data, packet_size);
7583 void process_event_update_packet(ubyte *data, header *hinfo)
7585 int offset = HEADER_LENGTH;
7591 store_flags = Mission_events[u_event].flags;
7592 GET_DATA(Mission_events[u_event].flags);
7593 GET_DATA(Mission_events[u_event].formula);
7594 GET_DATA(Mission_events[u_event].result);
7595 GET_DATA(Mission_events[u_event].count);
7598 // went from non directive special to directive special
7599 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7600 mission_event_set_directive_special(u_event);
7602 // if we went directive special to non directive special
7603 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7604 mission_event_unset_directive_special(u_event);
7608 // weapon detonate packet
7609 void send_weapon_detonate_packet(object *objp)
7611 ubyte data[MAX_PACKET_SIZE];
7612 int packet_size = 0;
7615 Assert(MULTIPLAYER_MASTER);
7616 if(!MULTIPLAYER_MASTER){
7619 Assert(objp != NULL);
7624 // build the header and add the data
7625 BUILD_HEADER(WEAPON_DET);
7626 ADD_DATA(objp->net_signature);
7628 // send to all players
7629 multi_io_send_to_all(data, packet_size);
7632 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7635 int offset = HEADER_LENGTH;
7636 object *objp = NULL;
7638 // get the weapon signature
7642 // lookup the weapon
7643 objp = multi_get_network_object(net_sig);
7644 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7645 weapon_detonate(objp);
7649 // flak fired packet
7650 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7653 ushort pnet_signature;
7654 ubyte data[MAX_PACKET_SIZE], cindex;
7660 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7664 // local setup -- be sure we are actually passing a weapon!!!!
7665 objp = &Objects[weapon_objnum];
7666 Assert ( objp->type == OBJ_WEAPON );
7667 pnet_signature = Objects[ship_objnum].net_signature;
7669 Assert( subsys_index < UCHAR_MAX );
7670 cindex = (ubyte)subsys_index;
7672 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7677 // build the fire turret packet.
7678 BUILD_HEADER(FLAK_FIRED);
7679 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.fvec);
7680 ADD_DATA( pnet_signature );
7682 val = (short)ssp->submodel_info_1.angs.h;
7684 val = (short)ssp->submodel_info_2.angs.p;
7686 ADD_DATA( flak_range );
7688 multi_io_send_to_all(data, packet_size);
7690 multi_rate_add(1, "flk", packet_size);
7693 void process_flak_fired_packet(ubyte *data, header *hinfo)
7695 int offset, weapon_objnum, wid;
7696 ushort pnet_signature;
7704 short pitch, heading;
7707 // get the data for the turret fired packet
7708 offset = HEADER_LENGTH;
7709 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7710 GET_DATA( pnet_signature );
7711 GET_DATA( turret_index );
7712 GET_DATA( heading );
7714 GET_DATA( flak_range );
7715 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7718 objp = multi_get_network_object( pnet_signature );
7719 if ( objp == NULL ) {
7720 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7724 // if this isn't a ship, do nothing
7725 if ( objp->type != OBJ_SHIP ){
7729 // make an orientation matrix from the o_fvec
7730 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7732 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7733 // hack, but should be suitable.
7734 shipp = &Ships[objp->instance];
7735 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7739 wid = ssp->system_info->turret_weapon_type;
7740 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7744 // bash the position and orientation of the turret
7745 ssp->submodel_info_1.angs.h = (float)heading;
7746 ssp->submodel_info_2.angs.p = (float)pitch;
7748 // get the world position of the weapon
7749 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7751 // create the weapon object
7752 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7753 if (weapon_objnum != -1) {
7754 if ( Weapon_info[wid].launch_snd != -1 ) {
7755 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7758 // create a muzzle flash from a flak gun based upon firing position and weapon type
7759 flak_muzzle_flash(&pos, &dir, wid);
7761 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7762 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7766 #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);
7767 #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);
7769 // player pain packet
7770 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7772 ubyte data[MAX_PACKET_SIZE];
7775 int packet_size = 0;
7777 Assert(MULTIPLAYER_MASTER);
7778 if(!MULTIPLAYER_MASTER){
7786 // build the packet and add the code
7787 BUILD_HEADER(NETPLAYER_PAIN);
7788 windex = (ubyte)weapon_info_index;
7790 udamage = (ushort)damage;
7793 ADD_DATA((*hitpos));
7795 // send to the player
7796 multi_io_send(pl, data, packet_size);
7798 multi_rate_add(1, "pai", packet_size);
7801 void process_player_pain_packet(ubyte *data, header *hinfo)
7807 vector local_hit_pos;
7810 // get the data for the pain packet
7811 offset = HEADER_LENGTH;
7815 GET_DATA(local_hit_pos);
7818 mprintf(("PAIN!\n"));
7820 // get weapon info pointer
7821 Assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER));
7822 if(! ((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
7825 wip = &Weapon_info[windex];
7827 // play the weapon hit sound
7828 Assert(Player_obj != NULL);
7829 if(Player_obj == NULL){
7832 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
7834 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
7835 ship_hit_pain((float)udamage);
7838 ship_apply_whack(&force, &local_hit_pos, Player_obj);
7842 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
7844 ubyte data[MAX_PACKET_SIZE];
7846 int packet_size = 0;
7848 // build the header and add the data
7849 BUILD_HEADER(LIGHTNING_PACKET);
7850 val = (char)bolt_type;
7853 ADD_DATA((*strike));
7855 // send to everyone unreliable for now
7856 multi_io_send_to_all(data, packet_size);
7859 void process_lightning_packet(ubyte *data, header *hinfo)
7863 vector start, strike;
7866 offset = HEADER_LENGTH;
7867 GET_DATA(bolt_type);
7878 nebl_bolt(bolt_type, &start, &strike);
7881 void send_bytes_recvd_packet(net_player *pl)
7883 // only clients should ever be doing this
7888 ubyte data[MAX_PACKET_SIZE];
7889 int packet_size = 0;
7890 BUILD_HEADER(BYTES_SENT);
7891 ADD_DATA(pl->cl_bytes_recvd);
7893 // send to the server
7894 multi_io_send_reliable(pl, data, packet_size);
7897 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
7901 net_player *pl = NULL;
7902 int offset = HEADER_LENGTH;
7908 if(Net_player == NULL){
7911 if(!MULTIPLAYER_MASTER){
7915 // make sure we know what player sent this
7916 pid = find_player_id(hinfo->id);
7917 if((pid < 0) || (pid >= MAX_PLAYERS)){
7920 pl = &Net_players[pid];
7923 pl->cl_bytes_recvd = bytes;
7927 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
7930 pl->sv_bytes_sent = 0;
7934 void send_host_captain_change_packet(short player_id, int captain_change)
7936 ubyte data[MAX_PACKET_SIZE];
7937 int packet_size = 0;
7940 BUILD_HEADER(TRANSFER_HOST);
7941 ADD_DATA(player_id);
7942 ADD_DATA(captain_change);
7945 multi_io_send_to_all_reliable(data, packet_size);
7948 void process_host_captain_change_packet(ubyte *data, header *hinfo)
7950 int offset = HEADER_LENGTH;
7951 int idx, found_player, captain_change;
7954 // get the player id
7955 GET_DATA(player_id);
7956 GET_DATA(captain_change);
7962 for(idx=0; idx<MAX_PLAYERS; idx++){
7963 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
7964 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
7969 // unflag all old players
7970 for(idx=0; idx<MAX_PLAYERS; idx++){
7971 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
7976 for(idx=0; idx<MAX_PLAYERS; idx++){
7977 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
7978 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
7980 // spew to the HUD config
7981 if(Net_players[idx].player != NULL){
7982 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
7992 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
7997 void send_self_destruct_packet()
7999 ubyte data[MAX_PACKET_SIZE];
8000 int packet_size = 0;
8003 if(Net_player == NULL){
8007 // if i'm the server, I shouldn't be here
8008 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8009 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8013 // only if this is valid
8014 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8019 if((Player_ship == NULL) || (Player_obj == NULL)){
8024 BUILD_HEADER(SELF_DESTRUCT);
8025 ADD_DATA(Player_obj->net_signature);
8027 // send to the server
8028 multi_io_send_reliable(Net_player, data, packet_size);
8031 void process_self_destruct_packet(ubyte *data, header *hinfo)
8033 int offset = HEADER_LENGTH;
8037 // get the net signature
8042 np_index = find_player_id(hinfo->id);
8046 if(MULTI_OBSERVER(Net_players[np_index])){
8049 if(Net_players[np_index].player == NULL){
8052 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8055 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8058 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8061 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8066 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);