2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Network/MultiMsgs.cpp $
15 * C file that holds functions for the building and processing of multiplayer packets
18 * Revision 1.4 2002/06/09 04:41:24 relnev
19 * added copyright header
21 * Revision 1.3 2002/05/26 20:49:54 theoddone33
24 * Revision 1.2 2002/05/07 03:16:47 theoddone33
25 * The Great Newline Fix
27 * Revision 1.1.1.1 2002/05/03 03:28:10 root
31 * 83 9/14/99 2:21p Dave
32 * Fixed observer mode joining and ingame stuff.
34 * 82 9/14/99 3:26a Dave
35 * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
36 * respawn-too-early problem. Made a few crash points safe.
38 * 81 9/13/99 4:52p Dave
41 * 80 9/08/99 10:01p Dave
42 * Make sure game won't run in a drive's root directory. Make sure
43 * standalone routes suqad war messages properly to the host.
45 * 79 8/28/99 4:54p Dave
46 * Fixed directives display for multiplayer clients for wings with
47 * multiple waves. Fixed hud threat indicator rendering color.
49 * 78 8/27/99 12:32a Dave
50 * Allow the user to specify a local port through the launcher.
52 * 77 8/26/99 8:51p Dave
53 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
55 * 76 8/25/99 4:38p Dave
56 * Updated PXO stuff. Make squad war report stuff much more nicely.
58 * 75 8/24/99 1:50a Dave
59 * Fixed client-side afterburner stuttering. Added checkbox for no version
60 * checking on PXO join. Made button info passing more friendly between
63 * 74 8/22/99 5:53p Dave
64 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
65 * instead of ship designations for multiplayer players.
67 * 73 8/22/99 1:55p Dave
68 * Cleaned up host/team-captain leaving code.
70 * 72 8/22/99 1:19p Dave
71 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
72 * which d3d cards are detected.
74 * 71 8/19/99 10:59a Dave
75 * Packet loss detection.
77 * 70 8/17/99 1:12p Dave
78 * Send TvT update when a client has finished joining so he stays nice and
81 * 69 8/16/99 4:05p Dave
82 * Big honking checkin.
84 * 68 8/11/99 5:54p Dave
85 * Fixed collision problem. Fixed standalone ghost problem.
87 * 67 8/06/99 9:46p Dave
88 * Hopefully final changes for the demo.
90 * 66 8/05/99 2:06a Dave
93 * 65 7/30/99 7:01p Dave
94 * Dogfight escort gauge. Fixed up laser rendering in Glide.
96 * 64 7/29/99 5:41p Jefff
97 * Sound hooks for cmeasure success
99 * 63 7/28/99 5:34p Dave
100 * Nailed the missing stats bug to the wall. Problem was optimized build
101 * and using GET_DATA() with array elements. BLECH.
103 * 62 7/26/99 5:50p Dave
104 * Revised ingame join. Better? We'll see....
106 * 61 7/24/99 1:54p Dave
107 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
110 * 60 7/22/99 7:17p Dave
111 * Fixed excessive whacks in multiplayer.
113 * 59 7/08/99 10:53a Dave
114 * New multiplayer interpolation scheme. Not 100% done yet, but still
115 * better than the old way.
117 * 58 7/03/99 5:50p Dave
118 * Make rotated bitmaps draw properly in padlock views.
120 * 57 7/03/99 4:08p Dave
121 * Fixed wss_slots size issues. Fixed potentially nasty bug in low level
124 * 56 6/21/99 7:24p Dave
125 * netplayer pain packet. Added type E unmoving beams.
127 * 55 6/18/99 5:16p Dave
128 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
129 * dialog to PXO screen.
131 * 54 6/16/99 4:06p Dave
132 * New pilot info popup. Added new draw-bitmap-as-poly function.
134 * 53 6/16/99 10:20a Dave
135 * Added send-message-list sexpression.
137 * 52 6/04/99 3:52p Anoop
138 * Removed bogus assert.
140 * 51 6/01/99 8:35p Dave
141 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
142 * awacs-set-radius sexpression.
144 * 50 5/21/99 5:03p Andsager
145 * Add code to display engine wash death. Modify ship_kill_packet
147 * 49 5/18/99 1:30p Dave
148 * Added muzzle flash table stuff.
150 * 48 5/14/99 1:59p Andsager
151 * Multiplayer message for subsystem cargo revealed.
153 * 47 5/14/99 12:15p Andsager
154 * Add vaporized to kill packet
156 * 46 5/03/99 8:32p Dave
157 * New version of multi host options screen.
159 * 45 4/30/99 12:18p Dave
160 * Several minor bug fixes.
162 * 44 4/29/99 2:29p Dave
163 * Made flak work much better in multiplayer.
165 * 43 4/28/99 11:13p Dave
166 * Temporary checkin of artillery code.
168 * 42 4/16/99 5:54p Dave
169 * Support for on/off style "stream" weapons. Real early support for
170 * target-painting lasers.
172 * 41 4/12/99 2:22p Dave
173 * More checks for dogfight stats.
175 * 40 4/09/99 2:21p Dave
176 * Multiplayer beta stuff. CD checking.
178 * 39 4/02/99 9:55a Dave
179 * Added a few more options in the weapons.tbl for beam weapons. Attempt
180 * at putting "pain" packets into multiplayer.
182 * 38 4/01/99 3:41p Anoop
183 * Removed bogus Int3().
185 * 37 3/19/99 9:51a Dave
186 * Checkin to repair massive source safe crash. Also added support for
187 * pof-style nebulae, and some new weapons code.
189 * 38 3/12/99 2:32p Anoop
190 * Removed bogus asserts.
192 * 37 3/11/99 11:41a Neilk
193 * Don't do multi_io_* operations in single-player
195 * 36 3/10/99 6:50p Dave
196 * Changed the way we buffer packets for all clients. Optimized turret
197 * fired packets. Did some weapon firing optimizations.
199 * 35 3/09/99 6:24p Dave
200 * More work on object update revamping. Identified several sources of
201 * unnecessary bandwidth.
203 * 34 3/08/99 7:03p Dave
204 * First run of new object update system. Looks very promising.
206 * 33 3/04/99 6:09p Dave
207 * Added in sexpressions for firing beams and checking for if a ship is
210 * 32 3/01/99 10:00a Dave
211 * Fxied several dogfight related stats bugs.
213 * 31 2/24/99 2:25p Dave
214 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
215 * bug for dogfight more.
217 * 30 2/23/99 2:29p Dave
218 * First run of oldschool dogfight mode.
220 * 29 2/21/99 6:01p Dave
221 * Fixed standalone WSS packets.
223 * 28 2/21/99 1:48p Dave
224 * Some code for monitoring datarate for multiplayer in detail.
226 * 27 2/17/99 2:11p Dave
227 * First full run of squad war. All freespace and tracker side stuff
230 * 26 2/12/99 6:16p Dave
231 * Pre-mission Squad War code is 95% done.
233 * 25 2/11/99 3:08p Dave
234 * PXO refresh button. Very preliminary squad war support.
236 * 24 1/29/99 5:07p Dave
237 * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
240 * 23 1/27/99 9:56a Dave
241 * Temporary checkin of beam weapons for Dan to make cool sounds.
243 * 22 1/26/99 6:33p Anoop
244 * Fixed multiplayer slot switching problem (be sure to remember that
245 * hinfo->id is player id# _not_ player index #)
247 * 21 1/24/99 11:37p Dave
248 * First full rev of beam weapons. Very customizable. Removed some bogus
249 * Int3()'s in low level net code.
251 * 20 1/15/99 4:37p Dave
252 * Potential fix for weapon pair problem.
254 * 19 1/14/99 6:06p Dave
255 * 100% full squad logo support for single player and multiplayer.
257 * 18 1/14/99 12:48a Dave
258 * Todo list bug fixes. Made a pass at putting briefing icons back into
259 * FRED. Sort of works :(
261 * 17 1/12/99 5:45p Dave
262 * Moved weapon pipeline in multiplayer to almost exclusively client side.
263 * Very good results. Bandwidth goes down, playability goes up for crappy
264 * connections. Fixed object update problem for ship subsystems.
266 * 16 1/08/99 4:56p Anoop
267 * Fixed a problem with wss request packets.
269 * 15 12/18/98 12:24p Markm
270 * Fixed a dumb bug where player image_filenames were not being passed
271 * properly in new players packet.
273 * 14 12/14/98 12:13p Dave
274 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
277 * 13 11/30/98 1:07p Dave
278 * 16 bit conversion, first run.
280 * 12 11/20/98 4:08p Dave
281 * Fixed flak effect in multiplayer.
283 * 11 11/19/98 4:19p Dave
284 * Put IPX sockets back in psnet. Consolidated all multiplayer config
287 * 10 11/19/98 8:04a Dave
288 * Full support for D3-style reliable sockets. Revamped packet lag/loss
289 * system, made it receiver side and at the lowest possible level.
291 * 9 11/17/98 11:12a Dave
292 * Removed player identification by address. Now assign explicit id #'s.
294 * 8 11/12/98 11:50a Dave
295 * Multiplayer clients set flak range to be very long.
297 * 7 11/12/98 12:13a Dave
298 * Tidied code up for multiplayer test. Put in network support for flak
301 * 6 11/05/98 5:55p Dave
302 * Big pass at reducing #includes
304 * 5 10/20/98 1:39p Andsager
305 * Make so sparks follow animated ship submodels. Modify
306 * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
307 * submodel_num. Add submodel_num to multiplayer hit packet.
309 * 4 10/13/98 9:29a Dave
310 * Started neatening up freespace.h. Many variables renamed and
311 * reorganized. Added AlphaColors.[h,cpp]
313 * 3 10/07/98 6:27p Dave
314 * Globalized mission and campaign file extensions. Removed Silent Threat
315 * special code. Moved \cache \players and \multidata into the \data
318 * 2 10/07/98 10:53a Dave
321 * 1 10/07/98 10:50a Dave
323 * 506 10/02/98 3:22p Allender
324 * fix up the -connect option and fix the -port option
326 * 505 10/02/98 11:45a Dave
327 * Fixed stupid chat message bug.
329 * 504 9/29/98 1:33p Dave
330 * Remove standalone only conditional compiles for pre 1.04 stuff.
332 * 503 9/28/98 1:54p Dave
333 * Make sure French and German don't xfer builtin files they don't have
336 * 502 9/20/98 7:19p Dave
337 * Added CHANGE_IFF packet.
339 * 501 9/17/98 3:08p Dave
340 * PXO to non-pxo game warning popup. Player icon stuff in create and join
341 * game screens. Upped server count refresh time in PXO to 35 secs (from
350 #include <io.h> // for findfirst/findnext, etc
353 #include "multimsgs.h"
354 #include "multiutil.h"
357 #include "multiteamselect.h"
358 #include "linklist.h"
359 #include "gamesequence.h"
360 #include "hudmessage.h"
361 #include "hudsquadmsg.h"
362 #include "freespace.h"
366 #include "missiongoals.h"
367 #include "missionparse.h"
368 #include "missionlog.h"
369 #include "missionmessage.h"
370 #include "missionbrief.h"
372 #include "cmeasure.h"
373 #include "model.h" // for some limits
374 #include "afterburner.h"
375 #include "stand_gui.h"
376 #include "multi_xfer.h"
382 #include "managepilot.h"
383 #include "hudsquadmsg.h"
385 #include "missionweaponchoice.h"
386 #include "missionshipchoice.h"
387 #include "fireballs.h"
390 #include "multi_ingame.h"
391 #include "multiteamselect.h"
393 #include "multi_campaign.h"
394 #include "multi_team.h"
395 #include "multi_respawn.h"
396 #include "multi_observer.h"
397 #include "multi_voice.h"
398 #include "asteroid.h"
399 #include "multi_pmsg.h"
400 #include "multi_data.h"
401 #include "multi_options.h"
402 #include "objcollide.h"
403 #include "hudreticle.h"
404 #include "multi_pause.h"
405 #include "multi_endgame.h"
406 #include "missiondebrief.h"
407 #include "multi_obj.h"
408 #include "multi_log.h"
410 #include "multi_kick.h"
414 #include "multi_rate.h"
415 #include "neblightning.h"
416 #include "hudescort.h"
418 // #define _MULTI_SUPER_WACKY_COMPRESSION
420 #ifdef _MULTI_SUPER_WACKY_COMPRESSION
422 #define MAX_CODE ( ( 1 << BITS ) - 1 )
423 #define TABLE_SIZE 35023L
424 #define END_OF_STREAM 256
425 #define BUMP_CODE 257
426 #define FLUSH_CODE 258
427 #define FIRST_CODE 259
436 static DICTIONARY dict[TABLE_SIZE];
437 static char decode_stack[TABLE_SIZE];
438 static uint next_code;
439 static int current_code_bits;
440 static uint next_bump_code;
442 typedef struct BitBuf {
448 void output_bits( BitBuf *bitbuf, uint code, int count )
452 mask = 1L << ( count - 1 );
455 bitbuf->rack |= bitbuf->mask;
457 if ( bitbuf->mask == 0 ) {
458 *bitbuf->data++=(ubyte)bitbuf->rack;
466 uint input_bits( BitBuf *bitbuf, int bit_count )
471 mask = 1L << ( bit_count - 1 );
474 if ( bitbuf->mask == 0x80 ) {
475 bitbuf->rack = *bitbuf->data++;
476 if ( bitbuf->rack == EOF )
477 return END_OF_STREAM;
479 if ( bitbuf->rack & bitbuf->mask )
480 return_value |= mask;
483 if ( bitbuf->mask == 0 )
486 return( return_value );
490 static void InitializeDictionary()
494 for ( i = 0 ; i < TABLE_SIZE ; i++ )
495 dict[i].code_value = UNUSED;
497 next_code = FIRST_CODE;
498 current_code_bits = 9;
499 next_bump_code = 511;
503 static uint find_child_node( int parent_code, int child_character )
508 index = ( child_character << ( BITS - 8 ) ) ^ parent_code;
512 offset = TABLE_SIZE - index;
514 if ( dict[ index ].code_value == UNUSED )
515 return( (uint) index );
516 if ( dict[ index ].parent_code == parent_code &&
517 dict[ index ].character == (char) child_character )
519 if ( (int) index >= offset )
522 index += TABLE_SIZE - offset;
527 static uint decode_string( uint count, uint code )
529 while ( code > 255 ) {
530 decode_stack[ count++ ] = dict[ code ].character;
531 code = dict[ code ].parent_code;
533 decode_stack[ count++ ] = (char) code;
537 int lzw_compress( ubyte *outputbuf, ubyte *inputbuf, int input_size )
545 // Init output bit buffer
548 output.data = outputbuf;
550 InitializeDictionary();
552 string_code = *inputbuf++;
554 for ( i=1 ; i<input_size ; i++ ) {
555 character = *inputbuf++;
556 index = find_child_node( string_code, character );
557 if ( dict[ index ].code_value != - 1 )
558 string_code = dict[ index ].code_value;
560 dict[ index ].code_value = next_code++;
561 dict[ index ].parent_code = string_code;
562 dict[ index ].character = (char) character;
563 output_bits( &output, (unsigned long) string_code, current_code_bits );
564 string_code = character;
565 if ( next_code > MAX_CODE ) {
566 output_bits( &output, (unsigned long) FLUSH_CODE, current_code_bits );
567 InitializeDictionary();
568 } else if ( next_code > next_bump_code ) {
569 output_bits( &output, (unsigned long) BUMP_CODE, current_code_bits );
571 next_bump_code <<= 1;
576 output_bits( &output, (unsigned long) string_code, current_code_bits );
577 output_bits( &output, (unsigned long) END_OF_STREAM, current_code_bits);
579 if ( output.mask != 0x80 )
580 *output.data++ = (ubyte)output.rack;
582 return output.data-outputbuf;
586 int lzw_expand( ubyte *outputbuf, ubyte *inputbuf )
597 input.data = inputbuf;
601 InitializeDictionary();
602 old_code = (uint) input_bits( &input, current_code_bits );
603 if ( old_code == END_OF_STREAM )
605 character = old_code;
606 outputbuf[counter++] = ( ubyte )old_code;
608 new_code = (uint) input_bits( &input, current_code_bits );
609 if ( new_code == END_OF_STREAM )
611 if ( new_code == FLUSH_CODE )
613 if ( new_code == BUMP_CODE ) {
617 if ( new_code >= next_code ) {
618 decode_stack[ 0 ] = (char) character;
619 count = decode_string( 1, old_code );
621 count = decode_string( 0, new_code );
623 character = decode_stack[ count - 1 ];
625 outputbuf[counter++] = ( ubyte )decode_stack[ --count ];
626 dict[ next_code ].parent_code = old_code;
627 dict[ next_code ].character = (char) character;
635 // send the specified data packet to all players
636 void multi_io_send(net_player *pl, ubyte *data, int len)
639 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
643 // don't do it for single player
644 if(!(Game_mode & GM_MULTIPLAYER)){
649 if(MULTIPLAYER_CLIENT){
650 // Assert(pl == Net_player);
651 if(pl != Net_player){
655 // Assert(pl != Net_player);
656 if(pl == Net_player){
661 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
662 if ((pl->s_info.unreliable_buffer_size + len) > MAX_PACKET_SIZE) {
663 multi_io_send_force(pl);
664 pl->s_info.unreliable_buffer_size = 0;
667 Assert((pl->s_info.unreliable_buffer_size + len) <= MAX_PACKET_SIZE);
669 memcpy(pl->s_info.unreliable_buffer + pl->s_info.unreliable_buffer_size, data, len);
670 pl->s_info.unreliable_buffer_size += len;
673 void multi_io_send_to_all(ubyte *data, int length, net_player *ignore)
676 Assert(MULTIPLAYER_MASTER);
678 // need to check for i > 1, hmmm... and connected. I don't know.
679 for (i = 0; i < MAX_PLAYERS; i++ ) {
680 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
684 // maybe ignore a player
685 if((ignore != NULL) && (&Net_players[i] == ignore)){
689 // ingame joiners not waiting to select a ship doesn't get any packets
690 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
695 multi_io_send(&Net_players[i], data, length);
699 void multi_io_send_force(net_player *pl)
702 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
706 // don't do it for single player
707 if(!(Game_mode & GM_MULTIPLAYER)){
711 // send everything in
712 if (MULTIPLAYER_MASTER) {
713 psnet_send(&pl->p_info.addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
715 // add the bytes sent to this player
716 pl->sv_bytes_sent += pl->s_info.unreliable_buffer_size;
718 psnet_send(&Netgame.server_addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
720 pl->s_info.unreliable_buffer_size = 0;
723 // send the data packet to all players via their reliable sockets
724 void multi_io_send_reliable(net_player *pl, ubyte *data, int len)
727 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
731 // don't do it for single player
732 if(!(Game_mode & GM_MULTIPLAYER)){
737 if(MULTIPLAYER_CLIENT){
738 // Assert(pl == Net_player);
739 if(pl != Net_player){
743 // Assert(pl != Net_player);
744 if(pl == Net_player){
749 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
750 if ((pl->s_info.reliable_buffer_size + len) > MAX_PACKET_SIZE) {
751 multi_io_send_reliable_force(pl);
752 pl->s_info.reliable_buffer_size = 0;
755 Assert((pl->s_info.reliable_buffer_size + len) <= MAX_PACKET_SIZE);
757 memcpy(pl->s_info.reliable_buffer + pl->s_info.reliable_buffer_size, data, len);
758 pl->s_info.reliable_buffer_size += len;
761 void multi_io_send_to_all_reliable(ubyte* data, int length, net_player *ignore)
764 Assert(MULTIPLAYER_MASTER);
766 // need to check for i > 1, hmmm... and connected. I don't know.
767 for (i = 0; i < MAX_PLAYERS; i++ ) {
768 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
772 // maybe ignore a player
773 if((ignore != NULL) && (&Net_players[i] == ignore)){
777 // ingame joiners not waiting to select a ship doesn't get any packets
778 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
783 multi_io_send_reliable(&Net_players[i], data, length);
787 void multi_io_send_reliable_force(net_player *pl)
790 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
794 // don't do it for single player
795 if(!(Game_mode & GM_MULTIPLAYER)){
799 // send everything in
800 if(MULTIPLAYER_MASTER) {
801 psnet_rel_send(pl->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
802 } else if(Net_player != NULL){
803 psnet_rel_send(Net_player->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
805 pl->s_info.reliable_buffer_size = 0;
808 // send all buffered packets
809 void multi_io_send_buffered_packets()
813 // don't do it for single player
814 if(!(Game_mode & GM_MULTIPLAYER)){
819 if(MULTIPLAYER_MASTER){
820 for(idx=0; idx<MAX_PLAYERS; idx++){
821 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
822 // force unreliable data
823 if(Net_players[idx].s_info.unreliable_buffer_size > 0){
824 multi_io_send_force(&Net_players[idx]);
825 Net_players[idx].s_info.unreliable_buffer_size = 0;
828 // force reliable data
829 if(Net_players[idx].s_info.reliable_buffer_size > 0){
830 multi_io_send_reliable_force(&Net_players[idx]);
831 Net_players[idx].s_info.reliable_buffer_size = 0;
837 else if(Net_player != NULL){
838 // force unreliable data
839 if(Net_player->s_info.unreliable_buffer_size > 0){
840 multi_io_send_force(Net_player);
841 Net_player->s_info.unreliable_buffer_size = 0;
844 // force reliable data
845 if(Net_player->s_info.reliable_buffer_size > 0){
846 multi_io_send_reliable_force(Net_player);
847 Net_player->s_info.reliable_buffer_size = 0;
852 // 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)
853 void send_game_chat_packet(net_player *from, char *msg, int msg_mode, net_player *to, char *expr, int server_msg)
855 ubyte data[MAX_PACKET_SIZE],mode;
858 BUILD_HEADER(GAME_CHAT);
861 ADD_DATA(from->player_id);
863 // add the message mode and if in MSG_TARGET mode, add who the target is
864 ADD_DATA(server_msg);
865 mode = (ubyte)msg_mode;
868 case MULTI_MSG_TARGET:
870 ADD_DATA(to->player_id);
873 Assert(expr != NULL);
877 // add the message itself
880 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
882 // message all players
884 for(idx=0;idx<MAX_PLAYERS;idx++){
885 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from)){
886 multi_io_send_reliable(&Net_players[idx], data, packet_size);
891 // message only friendly players
892 case MULTI_MSG_FRIENDLY:
893 for(idx=0;idx<MAX_PLAYERS;idx++){
894 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)){
895 multi_io_send_reliable(&Net_players[idx], data, packet_size);
900 // message only hostile players
901 case MULTI_MSG_HOSTILE:
902 for(idx=0;idx<MAX_PLAYERS;idx++){
903 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)){
904 multi_io_send_reliable(&Net_players[idx], data, packet_size);
909 // message the player's target
910 case MULTI_MSG_TARGET:
912 if(MULTI_CONNECTED((*to)) && !MULTI_STANDALONE((*to))){
913 multi_io_send_reliable(to, data, packet_size);
917 // message all players who match the expression string
919 Assert(expr != NULL);
920 for(idx=0;idx<MAX_PLAYERS;idx++){
921 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && multi_msg_matches_expr(&Net_players[idx],expr) ){
922 multi_io_send_reliable(&Net_players[idx], data, packet_size);
928 // send to the server, who will take care of routing it
930 multi_io_send_reliable(Net_player, data, packet_size);
934 // process a general game chat packet, if we're the standalone we should rebroadcast
935 void process_game_chat_packet( ubyte *data, header *hinfo )
939 int color_index,player_index,to_player_index,should_display,server_msg;
940 char msg[MULTI_MSG_MAX_TEXT_LEN+CALLSIGN_LEN+2];
944 offset = HEADER_LENGTH;
946 // get the id of the sender
949 // determine if this is a server message
950 GET_DATA(server_msg);
955 // if targeting a specific player, get the address
958 case MULTI_MSG_TARGET:
965 // get the message itself
969 // get the index of the sending player
970 color_index = find_player_id(from);
971 player_index = color_index;
973 // if we couldn't find the player - bail
974 if(player_index == -1){
975 nprintf(("Network","Could not find player for processing game chat packet!\n"));
981 // if we're the server, determine what to do with the packet here
982 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
983 // if he's targeting a specific player, find out who it is
984 if(mode == MULTI_MSG_TARGET){
985 to_player_index = find_player_id(to);
987 to_player_index = -1;
990 // if we couldn't find who sent the message or who should be getting the message, the bail
991 if(((to_player_index == -1) && (mode == MULTI_MSG_TARGET)) || (player_index == -1)){
995 // determine if _I_ should be seeing the text
996 if(Game_mode & GM_STANDALONE_SERVER){
999 // check against myself for several specific cases
1001 if((mode == MULTI_MSG_ALL) ||
1002 ((mode == MULTI_MSG_FRIENDLY) && (Net_player->p_info.team == Net_players[player_index].p_info.team)) ||
1003 ((mode == MULTI_MSG_HOSTILE) && (Net_player->p_info.team != Net_players[player_index].p_info.team)) ||
1004 ((mode == MULTI_MSG_TARGET) && (MY_NET_PLAYER_NUM == to_player_index)) ||
1005 ((mode == MULTI_MSG_EXPR) && multi_msg_matches_expr(Net_player,expr)) ){
1010 // if we're the server of a game, we need to rebroadcast to all other players
1012 // individual target mission
1013 case MULTI_MSG_TARGET:
1014 // if I was the inteneded target, or we couldn't find the intended target, don't rebroadcast
1015 if(to_player_index != MY_NET_PLAYER_NUM){
1016 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, &Net_players[to_player_index], NULL, server_msg);
1020 case MULTI_MSG_EXPR:
1021 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, expr, server_msg);
1025 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, NULL, server_msg);
1029 // if a client receives this packet, its always ok for him to display it
1034 // if we're not on a standalone
1036 if(server_msg == 2){
1039 multi_display_chat_msg(msg, player_index, !server_msg);
1044 // broadcast a hud message to all players
1045 void send_hud_msg_to_all( char* msg )
1047 ubyte data[MAX_PACKET_SIZE];
1050 // only the server should be sending this packet
1051 BUILD_HEADER(HUD_MSG);
1055 multi_io_send_to_all( data, packet_size );
1058 // process an incoming hud message packet
1059 void process_hud_message(ubyte* data, header* hinfo)
1062 char msg_buffer[255];
1064 offset = HEADER_LENGTH;
1066 GET_STRING(msg_buffer);
1069 // this is the only safe place to do this since only in the mission is the HUD guaranteed to be inited
1070 if(Game_mode & GM_IN_MISSION){
1071 HUD_printf(msg_buffer);
1075 // send a join packet request to the specified address (should be a server)
1076 void send_join_packet(net_addr* addr,join_request *jr)
1078 ubyte data[MAX_PACKET_SIZE];
1081 // build the header and add the request
1085 psnet_send(addr, data, packet_size);
1088 // process an incoming join request packet
1089 void process_join_packet(ubyte* data, header* hinfo)
1094 int host_restr_mode;
1095 // int team0_avail,team1_avail;
1096 char join_string[255];
1099 // only the server of the game should ever receive this packet
1100 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
1103 offset = HEADER_LENGTH;
1105 // read in the request info
1106 memset(&jr,0,sizeof(join_request));
1112 // fill in the address information of where this came from
1113 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
1115 // determine if we should accept this guy, or return a reason we should reject him
1116 // see the DENY_* codes in multi.h
1117 ret_code = multi_eval_join_request(&jr,&addr);
1119 // evaluate the return code
1121 // he should be accepted
1125 // we have to query the host because this is a restricted game
1126 case JOIN_QUERY_RESTRICTED :
1127 if(!(Game_mode & GM_STANDALONE_SERVER)){
1128 // notify the host of the event
1129 snd_play(&Snds[SND_CUE_VOICE]);
1132 // set the query timestamp
1133 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
1134 Netgame.flags |= NG_FLAG_INGAME_JOINING;
1136 // determine what mode we're in
1137 host_restr_mode = -1;
1138 memset(join_string,0,255);
1139 // if(Netgame.type == NG_TYPE_TEAM){
1140 // multi_player_ships_available(&team0_avail,&team1_avail);
1142 // if(team0_avail && team1_avail){
1143 // host_restr_mode = MULTI_JOIN_RESTR_MODE_4;
1144 // sprintf(join_string,"Player %s has tried to join. Accept on team 1 or 2 ?",jr.callsign);
1145 // } else if(team0_avail && !team1_avail){
1146 // host_restr_mode = MULTI_JOIN_RESTR_MODE_2;
1147 // sprintf(join_string,"Player %s has tried to join team 0, accept y/n ? ?",jr.callsign);
1148 // } else if(!team0_avail && team1_avail){
1149 // host_restr_mode = MULTI_JOIN_RESTR_MODE_3;
1150 // sprintf(join_string,"Player %s has tried to join team 1, accept y/n ?",jr.callsign);
1152 // } else if(Netgame.mode == NG_MODE_RESTRICTED){
1153 host_restr_mode = MULTI_JOIN_RESTR_MODE_1;
1154 sprintf(join_string,XSTR("Player %s has tried to join, accept y/n ?",715),jr.callsign);
1156 Assert(host_restr_mode != -1);
1158 // store the request info
1159 memcpy(&Multi_restr_join_request,&jr,sizeof(join_request));
1160 memcpy(&Multi_restr_addr,&addr,sizeof(net_addr));
1161 Multi_join_restr_mode = host_restr_mode;
1163 // if i'm the standalone server, I need to send a query to the host
1164 if(Game_mode & GM_STANDALONE_SERVER){
1165 send_host_restr_packet(jr.callsign,0,Multi_join_restr_mode);
1167 HUD_printf(join_string);
1171 ml_printf(NOX("Receive restricted join request from %s"), jr.callsign);
1175 // he'e being denied for some reason
1177 // send him the reason he is being denied
1178 send_deny_packet(&addr,ret_code);
1182 // process the rest of the request
1183 multi_process_valid_join_request(&jr,&addr);
1186 // send a notification that a new player has joined the game (if target != NULL, broadcast the packet)
1187 void send_new_player_packet(int new_player_num,net_player *target)
1189 ubyte data[MAX_PACKET_SIZE], val;
1190 int packet_size = 0;
1192 BUILD_HEADER( NOTIFY_NEW_PLAYER );
1194 // add the new player's info
1195 ADD_DATA(new_player_num);
1196 ADD_DATA(Net_players[new_player_num].p_info.addr);
1197 ADD_DATA(Net_players[new_player_num].player_id);
1198 ADD_DATA(Net_players[new_player_num].flags);
1199 ADD_STRING(Net_players[new_player_num].player->callsign);
1200 ADD_STRING(Net_players[new_player_num].player->image_filename);
1201 ADD_STRING(Net_players[new_player_num].player->squad_filename);
1202 ADD_STRING(Net_players[new_player_num].p_info.pxo_squad_name);
1204 val = (ubyte)Net_players[new_player_num].p_info.team;
1207 // broadcast the data
1209 multi_io_send_reliable(target, data, packet_size);
1211 multi_io_send_to_all_reliable(data, packet_size);
1215 // process a notification for a new player who has joined the game
1216 void process_new_player_packet(ubyte* data, header* hinfo)
1218 int already_in_game = 0;
1219 int offset, new_player_num,player_num,new_flags;
1221 char new_player_name[CALLSIGN_LEN+2] = "";
1222 char new_player_image[MAX_FILENAME_LEN+1] = "";
1223 char new_player_squad[MAX_FILENAME_LEN+1] = "";
1224 char new_player_pxo_squad[LOGIN_LEN+1] = "";
1225 char notify_string[256];
1229 offset = HEADER_LENGTH;
1231 // get the new players information
1232 GET_DATA(new_player_num);
1235 GET_DATA(new_flags);
1236 GET_STRING(new_player_name);
1237 GET_STRING(new_player_image);
1238 GET_STRING(new_player_squad);
1239 GET_STRING(new_player_pxo_squad);
1243 player_num = multi_find_open_player_slot();
1244 Assert(player_num != -1);
1246 // note that this new code does not check for duplicate IPs. It merely checks to see if
1247 // the slot referenced by new_player_num is already occupied by a connected player
1248 if(MULTI_CONNECTED(Net_players[new_player_num])){
1252 // if he's not alreayd in the game for one reason or another
1253 if ( !already_in_game ) {
1254 if ( Game_mode & GM_IN_MISSION ){
1255 HUD_sourced_printf(HUD_SOURCE_COMPUTER, XSTR("%s has entered the game\n",716), new_player_name);
1258 // create the player
1259 memcpy(new_addr.net_id, Psnet_my_addr.net_id, 4);
1261 if(new_flags & NETINFO_FLAG_OBSERVER){
1262 multi_obs_create_player(new_player_num,new_player_name,&new_addr,&Players[player_num]);
1263 Net_players[new_player_num].flags |= new_flags;
1265 multi_create_player( new_player_num, &Players[player_num],new_player_name, &new_addr, -1, new_id );
1266 Net_players[new_player_num].flags |= new_flags;
1269 // copy in the filename
1270 if(strlen(new_player_image) > 0){
1271 strcpy(Net_players[new_player_num].player->image_filename, new_player_image);
1273 strcpy(Net_players[new_player_num].player->image_filename, "");
1275 // copy his pilot squad filename
1276 Net_players[new_player_num].player->insignia_texture = -1;
1277 player_set_squad_bitmap(Net_players[new_player_num].player, new_player_squad);
1279 // copy in his pxo squad name
1280 strcpy(Net_players[new_player_num].p_info.pxo_squad_name, new_player_pxo_squad);
1282 // since we just created the player, set the last_heard_time here.
1283 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1285 Net_players[new_player_num].p_info.team = team;
1287 Net_players[new_player_num].player_id = new_id;
1289 // zero out this players ping
1290 multi_ping_reset(&Net_players[new_player_num].s_info.ping);
1292 // add a chat message
1293 if(Net_players[new_player_num].player->callsign != NULL){
1294 sprintf(notify_string,XSTR("<%s has joined>",717),Net_players[new_player_num].player->callsign);
1295 multi_display_chat_msg(notify_string,0,0);
1300 ml_printf(NOX("Received notification of new player %s"), Net_players[new_player_num].player->callsign);
1302 // let the current ui screen know someone joined
1303 switch(gameseq_get_state()){
1304 case GS_STATE_MULTI_HOST_SETUP :
1305 multi_create_handle_join(&Net_players[new_player_num]);
1307 case GS_STATE_MULTI_CLIENT_SETUP :
1308 multi_jw_handle_join(&Net_players[new_player_num]);
1313 #define PLAYER_DATA_SLOP 100
1315 void send_accept_player_data( net_player *npp, int is_ingame )
1319 ubyte data[MAX_PACKET_SIZE], stop;
1321 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1323 // add in the netplayer data for all players
1325 for (i=0; i<MAX_PLAYERS; i++) {
1326 // skip non connected players
1327 if ( !MULTI_CONNECTED(Net_players[i]) ){
1331 // skip this new player's entry
1332 if ( npp->player_id == Net_players[i].player_id ){
1336 // add the stop byte
1339 // add the player's number
1342 // add the player's address
1343 ADD_DATA(Net_players[i].p_info.addr);
1346 ADD_DATA(Net_players[i].player_id);
1349 ADD_STRING(Net_players[i].player->callsign);
1351 // add his image filename
1352 ADD_STRING(Net_players[i].player->image_filename);
1354 // add his squad filename
1355 ADD_STRING(Net_players[i].player->squad_filename);
1357 // add his PXO squad name
1358 ADD_STRING(Net_players[i].p_info.pxo_squad_name);
1361 ADD_DATA(Net_players[i].flags);
1363 // add his object's net sig
1365 ADD_DATA( Objects[Net_players[i].player->objnum].net_signature );
1368 if ( (packet_size + PLAYER_DATA_SLOP) > MAX_PACKET_SIZE ) {
1369 stop = APD_END_PACKET;
1371 multi_io_send_reliable( npp, data, packet_size );
1372 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1378 // add the stop byte
1379 stop = APD_END_DATA;
1381 multi_io_send_reliable(npp, data, packet_size);
1384 // send an accept packet to a client in response to a request to join the game
1385 void send_accept_packet(int new_player_num, int code, int ingame_join_team)
1388 ubyte data[MAX_PACKET_SIZE],val;
1389 char notify_string[256];
1392 Assert(new_player_num >= 0);
1394 // setup his "reliable" socket
1395 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1397 // build the packet header
1399 BUILD_HEADER(ACCEPT);
1401 // add the accept code
1404 // add code specific accept data
1405 if (code & ACCEPT_INGAME) {
1406 // the game filename
1407 ADD_STRING(Game_current_mission_filename);
1409 // if he is joining on a specific team, mark it here
1410 if(ingame_join_team != -1){
1413 val = (ubyte)ingame_join_team;
1421 if (code & ACCEPT_OBSERVER) {
1422 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1425 if (code & ACCEPT_HOST) {
1426 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1429 if (code & ACCEPT_CLIENT) {
1430 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1433 // add the current skill level setting on the host
1434 ADD_DATA(Game_skill_level);
1436 // add this guys player num
1437 ADD_DATA(new_player_num);
1439 // add his player id
1440 ADD_DATA(Net_players[new_player_num].player_id);
1442 // add netgame type flags
1443 ADD_DATA(Netgame.type_flags);
1446 // char buffer[100];
1447 // nprintf(("Network", "About to send accept packet to %s on port %d\n", get_text_address(buffer, addr->addr), addr->port ));
1450 // actually send the packet
1451 psnet_send(&Net_players[new_player_num].p_info.addr, data, packet_size);
1453 // if he's not an observer, inform all the other players in the game about him
1454 // inform the other players in the game about this new player
1455 for (i=0; i<MAX_PLAYERS; i++) {
1456 // skip unconnected players as well as this new guy himself
1457 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])) {
1461 // send the new packet
1462 send_new_player_packet(new_player_num,&Net_players[i]);
1465 // add a chat message
1466 if(Net_players[new_player_num].player->callsign != NULL){
1467 sprintf(notify_string,XSTR("<%s has joined>",717), Net_players[new_player_num].player->callsign);
1468 multi_display_chat_msg(notify_string, 0, 0);
1471 // handle any team vs. team details
1472 if (!(code & ACCEPT_OBSERVER)) {
1473 multi_team_handle_join(&Net_players[new_player_num]);
1477 if(Net_players[new_player_num].tracker_player_id >= 0){
1478 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);
1480 ml_printf(NOX("Server accepted %s as new client"), Net_players[new_player_num].player->callsign);
1485 // process the player data from the server
1486 void process_accept_player_data( ubyte *data, header *hinfo )
1488 int offset, player_num, player_slot_num, new_flags;
1489 char name[CALLSIGN_LEN + 1] = "";
1490 char image_name[MAX_FILENAME_LEN + 1] = "";
1491 char squad_name[MAX_FILENAME_LEN + 1] = "";
1492 char pxo_squad_name[LOGIN_LEN+1] = "";
1496 ushort ig_signature;
1498 offset = HEADER_LENGTH;
1501 while ( stop == APD_NEXT ) {
1502 player_slot_num = multi_find_open_player_slot();
1503 Assert(player_slot_num != -1);
1505 // get the player's number
1506 GET_DATA(player_num);
1508 // add the player's address
1511 // get the player's id#
1512 GET_DATA(player_id);
1517 // add his image filename
1518 GET_STRING(image_name);
1520 // get his squad logo filename
1521 GET_STRING(squad_name);
1523 // get his PXO squad name
1524 GET_STRING(pxo_squad_name);
1527 GET_DATA(new_flags);
1529 if (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) {
1530 if (!multi_obs_create_player(player_num, name, &addr, &Players[player_slot_num])) {
1535 // the error handling here is less than stellar. We should probably put up a popup and go
1536 // back to the main menu. But then again, this should never ever happen!
1537 if ( !multi_create_player(player_num, &Players[player_slot_num],name, &addr, -1, player_id) ) {
1542 // copy his image filename
1543 strcpy(Net_players[player_num].player->image_filename, image_name);
1545 // copy his pilot squad filename
1546 Net_players[player_num].player->insignia_texture = -1;
1547 player_set_squad_bitmap(Net_players[player_num].player, squad_name);
1549 // copy his pxo squad name
1550 strcpy(Net_players[player_num].p_info.pxo_squad_name, pxo_squad_name);
1552 // set his player id#
1553 Net_players[player_num].player_id = player_id;
1555 // mark him as being connected
1556 Net_players[player_num].flags |= NETINFO_FLAG_CONNECTED;
1557 Net_players[player_num].flags |= new_flags;
1559 // set the server pointer
1560 if ( Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER ) {
1561 Netgame.server = &Net_players[player_num];
1562 Netgame.server->last_heard_time = timer_get_fixed_seconds();
1564 // also - always set the server address to be where this data came from, NOT from
1565 // the data in the packet
1566 fill_net_addr(&Net_players[player_num].p_info.addr, hinfo->addr, hinfo->net_id, hinfo->port);
1569 // set the host pointer
1570 if ( Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST ) {
1571 Netgame.host = &Net_players[player_num];
1574 // read in the player's object net signature and store as his objnum for now
1575 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME ) {
1576 GET_DATA( ig_signature );
1577 Net_players[player_num].player->objnum = ig_signature;
1580 // get the stop byte
1585 if ( stop == APD_END_DATA ) {
1586 // if joining a game automatically, set the connect address to NULl so we don't try and
1587 // do this next time we enter a game
1588 if (Cmdline_connect_addr != NULL) {
1589 Cmdline_connect_addr = NULL;
1592 // send my stats to the server if I'm not in observer mode
1593 if (!(Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER)) {
1594 send_player_stats_block_packet(Net_player, STATS_ALLTIME);
1597 // if i'm being accepted as a host, then move into the host setup state
1598 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_HOST) {
1599 // set my permission bits
1600 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
1601 Net_player->state = NETPLAYER_STATE_STD_HOST_SETUP;
1603 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
1606 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER) {
1607 Net_player->flags |= NETINFO_FLAG_OBSERVER;
1609 // since observers can join 1 of 2 ways, only do this if we're not doing an ingame observer join
1610 if ( !(Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) ) {
1611 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1615 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_CLIENT) {
1616 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1619 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) {
1620 // flag myself as being an ingame joiner
1621 Net_player->flags |= NETINFO_FLAG_INGAME_JOIN;
1623 // move myself into the ingame join mission sync state
1624 Multi_sync_mode = MULTI_SYNC_INGAME;
1625 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
1628 // update my options on the server
1629 multi_options_update_local();
1631 // if we're in PXO mode, mark it down in our player struct
1632 if(MULTI_IS_TRACKER_GAME){
1633 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1634 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1639 // process an accept packet from the server
1640 extern int Select_default_ship;
1642 void process_accept_packet(ubyte* data, header* hinfo)
1644 int code, my_player_num, offset;
1648 // get the accept code
1649 offset = HEADER_LENGTH;
1653 // read in the accept code specific data
1655 if (code & ACCEPT_INGAME) {
1656 // the game filename
1657 GET_STRING(Game_current_mission_filename);
1658 Select_default_ship = 0;
1660 // determine if I'm being placed on a team
1667 if (code & ACCEPT_OBSERVER) {
1668 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1671 if (code & ACCEPT_HOST) {
1672 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1675 if (code & ACCEPT_CLIENT) {
1676 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1679 // fill in the netgame server address
1680 fill_net_addr( &Netgame.server_addr, hinfo->addr, hinfo->net_id, hinfo->port );
1682 // get the skill level setting
1683 GET_DATA(Game_skill_level);
1685 // get my netplayer number
1686 GET_DATA(my_player_num);
1689 GET_DATA(player_id);
1691 // get netgame type flags
1692 GET_DATA(Netgame.type_flags);
1694 // setup the Net_players structure for myself first
1695 Net_player = &Net_players[my_player_num];
1696 Net_player->flags = 0;
1697 Net_player->tracker_player_id = Multi_tracker_id;
1698 Net_player->player_id = player_id;
1699 Net_player->s_info.xfer_handle = -1;
1700 // stuff_netplayer_info( Net_player, &Psnet_my_addr, Ships[Objects[Player->objnum].instance].ship_info_index, Player );
1701 stuff_netplayer_info( Net_player, &Psnet_my_addr, 0, Player );
1702 multi_options_local_load(&Net_player->p_info.options, Net_player);
1704 Net_player->p_info.team = team;
1707 // determine if I have a CD
1709 Net_player->flags |= NETINFO_FLAG_HAS_CD;
1712 // set accept code in netplayer for this guy
1713 if ( code & ACCEPT_INGAME ){
1714 Net_player->flags |= NETINFO_FLAG_ACCEPT_INGAME;
1716 if ( code & ACCEPT_OBSERVER ){
1717 Net_player->flags |= NETINFO_FLAG_ACCEPT_OBSERVER;
1719 if ( code & ACCEPT_HOST ){
1720 Net_player->flags |= NETINFO_FLAG_ACCEPT_HOST;
1722 if ( code & ACCEPT_CLIENT ){
1723 Net_player->flags |= NETINFO_FLAG_ACCEPT_CLIENT;
1726 // if I have hacked data
1727 if(game_hacked_data()){
1728 Net_player->flags |= NETINFO_FLAG_HAXOR;
1731 // if we're supposed to flush our local data cache, do so now
1732 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
1733 multi_flush_multidata_cache();
1736 Net_player->sv_bytes_sent = 0;
1737 Net_player->sv_last_pl = -1;
1738 Net_player->cl_bytes_recvd = 0;
1739 Net_player->cl_last_pl = -1;
1741 // intiialize endgame stuff
1742 multi_endgame_init();
1746 // make a call to psnet to initialize and try to connect with the server.
1747 psnet_rel_connect_to_server( &Net_player->reliable_socket, &Netgame.server_addr );
1748 if ( Net_player->reliable_socket == INVALID_SOCKET ) {
1749 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONNECT_FAIL);
1753 // send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet)
1754 void send_leave_game_packet(short player_id, int kicked_reason, net_player *target)
1756 ubyte data[MAX_PACKET_SIZE];
1758 int packet_size = 0;
1760 BUILD_HEADER(LEAVE_GAME);
1762 // add a flag indicating whether he was kicked or not
1763 val = (char)kicked_reason;
1766 if (player_id < 0) {
1767 ADD_DATA(Net_player->player_id);
1769 // inform the host that we are leaving the game
1770 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1771 multi_io_send_to_all_reliable(data, packet_size);
1773 multi_io_send_reliable(Net_player, data, packet_size);
1776 // this is the case where to server is tossing a player (or indicating a respawned player has quit or become an observer)
1777 // so he has to tell everyone that this guy left
1779 nprintf(("Network","Sending a leave game packet to all players (server)\n"));
1781 // a couple of important checks
1782 Assert(player_id != Net_player->player_id);
1783 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1785 // add the id of the guy to be kicked
1786 ADD_DATA(player_id);
1788 // broadcast to everyone
1789 if (target == NULL) {
1790 multi_io_send_to_all_reliable(data, packet_size);
1792 multi_io_send_reliable(target, data, packet_size);
1797 // process a notification the a player has left the game
1798 void process_leave_game_packet(ubyte* data, header* hinfo)
1806 offset = HEADER_LENGTH;
1808 // get whether he was kicked
1809 GET_DATA(kicked_reason);
1811 // get the address of the guy who is to leave
1812 GET_DATA(deader_id);
1815 // determine who is dropping and printf out a notification
1816 player_num = find_player_id(deader_id);
1817 if (player_num == -1) {
1818 nprintf(("Network", "Received leave game packet for unknown player, ignoring\n"));
1822 nprintf(("Network", "Received a leave game notice for %s\n", Net_players[player_num].player->callsign));
1825 // a hook to display that a player was kicked
1826 if (kicked_reason >= 0){
1827 // if it was me that was kicked, leave the game
1828 if((Net_player != NULL) && (Net_player->player_id == deader_id)){
1831 switch(kicked_reason){
1832 case KICK_REASON_BAD_XFER:
1833 notify_code = MULTI_END_NOTIFY_KICKED_BAD_XFER;
1835 case KICK_REASON_CANT_XFER:
1836 notify_code = MULTI_END_NOTIFY_KICKED_CANT_XFER;
1838 case KICK_REASON_INGAME_ENDED:
1839 notify_code = MULTI_END_NOTIFY_KICKED_INGAME_ENDED;
1842 notify_code = MULTI_END_NOTIFY_KICKED;
1846 multi_quit_game(PROMPT_NONE, notify_code);
1849 // otherwise indicate someone was kicked
1851 nprintf(("Network","%s was kicked\n",Net_players[player_num].player->callsign));
1853 // display the result
1854 memset(str, 0, 512);
1855 multi_kick_get_text(&Net_players[player_num], kicked_reason, str);
1856 multi_display_chat_msg(str, player_num, 0);
1860 // first of all, if we're the master, we should be rebroadcasting this packet
1861 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1864 sprintf(msg, XSTR("%s has left the game",719), Net_players[player_num].player->callsign );
1866 if (!(Game_mode & GM_STANDALONE_SERVER)){
1867 HUD_sourced_printf(HUD_SOURCE_HIDDEN, msg);
1870 send_hud_msg_to_all(msg);
1871 multi_io_send_to_all_reliable(data, offset);
1874 // leave the game if the host and/or master has dropped
1876 if (((Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER) || (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)) ) {
1877 nprintf(("Network","Host and/or server has left the game - aborting...\n"));
1880 ml_string(NOX("Host and/or server has left the game"));
1882 // if the host leaves in the debriefing state, we should still wait until the player selects accept before we quit
1883 if (gameseq_get_state() != GS_STATE_DEBRIEF) {
1884 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
1887 delete_player(player_num);
1890 delete_player(player_num);
1892 // OSAPI GUI stuff (if standalone)
1893 if (Game_mode & GM_STANDALONE_SERVER) {
1894 // returns true if we should reset the standalone
1895 if (std_remove_player(&Net_players[player_num])) {
1896 nprintf(("Network", "Should reset!!\n"));
1900 // update these gui vals
1901 std_connect_set_host_connect_status();
1902 std_connect_set_connect_count();
1906 // send information about this currently active game to the specified address
1907 void send_game_active_packet(net_addr* addr)
1911 ubyte data[MAX_PACKET_SIZE],val;
1913 // build the header and add the data
1914 BUILD_HEADER(GAME_ACTIVE);
1916 // add the server version and compatible version #
1917 val = MULTI_FS_SERVER_VERSION;
1919 val = MULTI_FS_SERVER_COMPATIBLE_VERSION;
1922 ADD_STRING(Netgame.name);
1923 ADD_STRING(Netgame.mission_name);
1924 ADD_STRING(Netgame.title);
1925 val = (ubyte)multi_num_players();
1928 // add the proper flags
1930 if((Netgame.mode == NG_MODE_PASSWORD) || ((Game_mode & GM_STANDALONE_SERVER) && (multi_num_players() == 0) && (std_is_host_passwd()))){
1931 flags |= AG_FLAG_PASSWD;
1934 // proper netgame type flags
1935 if(Netgame.type_flags & NG_TYPE_TEAM){
1936 flags |= AG_FLAG_TEAMS;
1937 } else if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
1938 flags |= AG_FLAG_DOGFIGHT;
1940 flags |= AG_FLAG_COOP;
1943 // proper netgame state flags
1944 switch(Netgame.game_state){
1945 case NETGAME_STATE_FORMING:
1946 flags |= AG_FLAG_FORMING;
1949 case NETGAME_STATE_BRIEFING:
1950 case NETGAME_STATE_MISSION_SYNC:
1951 case NETGAME_STATE_HOST_SETUP:
1952 flags |= AG_FLAG_BRIEFING;
1955 case NETGAME_STATE_IN_MISSION:
1956 flags |= AG_FLAG_IN_MISSION;
1959 case NETGAME_STATE_PAUSED:
1960 flags |= AG_FLAG_PAUSE;
1963 case NETGAME_STATE_ENDGAME:
1964 case NETGAME_STATE_DEBRIEF:
1965 flags |= AG_FLAG_DEBRIEF;
1969 // if this is a standalone
1970 if(Game_mode & GM_STANDALONE_SERVER){
1971 flags |= AG_FLAG_STANDALONE;
1974 // if we're in campaign mode
1975 if(Netgame.campaign_mode == MP_CAMPAIGN){
1976 flags |= AG_FLAG_CAMPAIGN;
1979 // add the data about the connection speed of the host machine
1980 Assert( (Multi_connection_speed >= 0) && (Multi_connection_speed <= 4) );
1981 flags |= (Multi_connection_speed << AG_FLAG_CONNECTION_BIT);
1986 psnet_send(addr, data, packet_size);
1989 // process information about an active game
1990 void process_game_active_packet(ubyte* data, header* hinfo)
1995 int modes_compatible;
1997 fill_net_addr(&ag.server_addr, hinfo->addr, hinfo->net_id, hinfo->port);
1999 // read this game into a temporary structure
2000 offset = HEADER_LENGTH;
2002 // get the server version and compatible version
2003 GET_DATA(ag.version);
2004 GET_DATA(ag.comp_version);
2006 GET_STRING(ag.name);
2007 GET_STRING(ag.mission_name);
2008 GET_STRING(ag.title);
2010 ag.num_players = val;
2015 modes_compatible = 1;
2017 if((ag.flags & AG_FLAG_TRACKER) && !Multi_options_g.pxo){
2018 modes_compatible = 0;
2020 if(!(ag.flags & AG_FLAG_TRACKER) && Multi_options_g.pxo){
2021 modes_compatible = 0;
2025 // if this is a compatible version, and our modes are compatible, register it
2026 if( (ag.version == MULTI_FS_SERVER_VERSION) && modes_compatible ){
2027 multi_update_active_games(&ag);
2031 // send_game_update_packet sends an updated Netgame structure to all players currently connected. The update
2032 // is used to change the current mission, current state, etc.
2033 void send_netgame_update_packet(net_player *pl)
2037 ubyte data[MAX_PACKET_SIZE];
2040 BUILD_HEADER(GAME_UPDATE);
2042 // with new mission description field, this becomes way to large
2043 // so we must add every element piece by piece except the
2044 ADD_STRING(Netgame.name);
2045 ADD_STRING(Netgame.mission_name);
2046 ADD_STRING(Netgame.title);
2047 ADD_STRING(Netgame.campaign_name);
2048 ADD_DATA(Netgame.campaign_mode);
2049 ADD_DATA(Netgame.max_players);
2050 ADD_DATA(Netgame.security);
2051 ADD_DATA(Netgame.respawn);
2052 ADD_DATA(Netgame.flags);
2053 ADD_DATA(Netgame.type_flags);
2054 ADD_DATA(Netgame.version_info);
2055 ADD_DATA(Netgame.debug_flags);
2057 // only the server should ever send the netgame state (standalone situation)
2058 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2059 ADD_DATA(Netgame.game_state);
2062 // if we're the host on a standalone, send to the standalone and let him rebroadcast
2063 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2065 multi_io_send_to_all_reliable(data, packet_size);
2067 for(idx=0; idx<MAX_PLAYERS; idx++){
2068 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
2069 send_netgame_descript_packet(&Net_players[idx].p_info.addr, 1);
2073 multi_io_send_reliable(pl, data, packet_size);
2074 send_netgame_descript_packet( &pl->p_info.addr , 1 );
2077 Assert( pl == NULL ); // I don't think that a host in a standalone game would get here.
2078 multi_io_send_reliable(Net_player, data, packet_size);
2081 // host should always send a netgame options update as well
2082 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2083 multi_options_update_netgame();
2087 // process information about the netgame sent from the server/host
2088 void process_netgame_update_packet( ubyte *data, header *hinfo )
2090 int offset,old_flags;
2093 Assert(!(Game_mode & GM_STANDALONE_SERVER));
2094 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
2096 // read in the netgame information
2097 offset = HEADER_LENGTH;
2098 GET_STRING(Netgame.name);
2099 GET_STRING(Netgame.mission_name);
2100 GET_STRING(Netgame.title);
2101 GET_STRING(Netgame.campaign_name);
2102 GET_DATA(Netgame.campaign_mode);
2103 GET_DATA(Netgame.max_players); // ignore on the standalone, who keeps track of this himself
2104 GET_DATA(Netgame.security);
2105 GET_DATA(Netgame.respawn);
2107 // be sure not to blast the quitting flag because of the "one frame extra" problem
2108 old_flags = Netgame.flags;
2109 GET_DATA(Netgame.flags);
2110 GET_DATA(Netgame.type_flags);
2111 GET_DATA(Netgame.version_info);
2112 GET_DATA(Netgame.debug_flags);
2119 // now compare the passed in game state to our current known state. If it has changed, then maybe
2120 // do something interesting.
2121 // move from the forming or debriefing state to the mission sync state
2122 if ( ng_state == NETGAME_STATE_MISSION_SYNC ){
2123 // if coming from the forming state
2124 if( (Netgame.game_state == NETGAME_STATE_FORMING) ||
2125 ((Netgame.game_state != NETGAME_STATE_FORMING) && ((gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP) || (gameseq_get_state() == GS_STATE_MULTI_CLIENT_SETUP))) ){
2126 // do any special processing for forced state transitions
2127 multi_handle_state_special();
2129 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2130 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2131 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2133 // if coming from the debriefing state
2134 else if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2135 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2137 // do any special processing for forced state transitions
2138 multi_handle_state_special();
2140 multi_flush_mission_stuff();
2142 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2143 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2144 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2147 // move from mission sync to team select
2148 else if ( ng_state == NETGAME_STATE_BRIEFING ){
2149 if( (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ||
2150 ((Netgame.game_state != NETGAME_STATE_MISSION_SYNC) && (gameseq_get_state() == GS_STATE_MULTI_MISSION_SYNC) && (Multi_sync_mode != MULTI_SYNC_POST_BRIEFING)) ){
2152 // do any special processing for forced state transitions
2153 multi_handle_state_special();
2155 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2156 gameseq_post_event(GS_EVENT_START_BRIEFING);
2159 // move from the debriefing to the create game screen
2160 else if ( ng_state == NETGAME_STATE_FORMING ){
2161 if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2162 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2163 // do any special processing for forced state transitions
2164 multi_handle_state_special();
2166 multi_flush_mission_stuff();
2168 // move to the proper screen
2169 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2170 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2172 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
2177 Netgame.game_state = ng_state;
2180 // send a request or a reply for mission description, if code == 0, request, if code == 1, reply
2181 void send_netgame_descript_packet(net_addr *addr, int code)
2183 ubyte data[MAX_PACKET_SIZE],val;
2185 int packet_size = 0;
2188 BUILD_HEADER(UPDATE_DESCRIPT);
2194 // add as much of the description as we dare
2195 len = strlen(The_mission.mission_desc);
2196 if(len > MAX_PACKET_SIZE - 10){
2197 len = MAX_PACKET_SIZE - 10;
2199 memcpy(data+packet_size,The_mission.mission_desc,len);
2202 ADD_STRING(The_mission.mission_desc);
2206 Assert(addr != NULL);
2208 psnet_send(addr, data, packet_size);
2212 // process an incoming netgame description packet
2213 void process_netgame_descript_packet( ubyte *data, header *hinfo )
2217 char mission_desc[MISSION_DESC_LENGTH+2];
2220 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2222 // read this game into a temporary structure
2223 offset = HEADER_LENGTH;
2226 // if this is a request for mission description
2228 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2233 // send an update to this guy
2234 send_netgame_descript_packet(&addr, 1);
2236 memset(mission_desc,0,MISSION_DESC_LENGTH+2);
2237 GET_STRING(mission_desc);
2239 // only display if we're in the proper state
2240 state = gameseq_get_state();
2242 case GS_STATE_MULTI_JOIN_GAME:
2243 case GS_STATE_MULTI_CLIENT_SETUP:
2244 case GS_STATE_MULTI_HOST_SETUP:
2245 multi_common_set_text(mission_desc);
2253 // broadcast a query for active games. IPX will use net broadcast and TCP will either request from the MT or from the specified list
2254 void broadcast_game_query()
2258 server_item *s_moveup;
2259 ubyte data[MAX_PACKET_SIZE];
2261 BUILD_HEADER(GAME_QUERY);
2263 // go through the server list and query each of those as well
2264 s_moveup = Game_server_head;
2265 if(s_moveup != NULL){
2267 send_server_query(&s_moveup->server_addr);
2268 s_moveup = s_moveup->next;
2269 } while(s_moveup != Game_server_head);
2272 fill_net_addr(&addr, Psnet_my_addr.addr, Psnet_my_addr.net_id, DEFAULT_GAME_PORT);
2274 // send out a broadcast if our options allow us
2275 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2276 psnet_broadcast( &addr, data, packet_size);
2280 // send an individual query to an address to see if there is an active game
2281 void send_server_query(net_addr *addr)
2284 ubyte data[MAX_PACKET_SIZE];
2286 // build the header and send the data
2287 BUILD_HEADER(GAME_QUERY);
2288 psnet_send(addr, data, packet_size);
2291 // process a query from a client looking for active freespace games
2292 void process_game_query(ubyte* data, header* hinfo)
2297 offset = HEADER_LENGTH;
2301 // check to be sure that we don't capture our own broadcast message
2302 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2303 if ( psnet_same( &addr, &Psnet_my_addr) ){
2307 // if I am not a server of a game, don't send a reply!!!
2308 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
2312 // if the game options are being selected, then ignore the request
2313 // also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
2314 if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
2318 // send information about this active game
2319 send_game_active_packet(&addr);
2322 // sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
2323 void send_netplayer_update_packet( net_player *pl )
2325 int packet_size,idx;
2326 ubyte data[MAX_PACKET_SIZE],val;
2328 BUILD_HEADER(NETPLAYER_UPDATE);
2330 // if I'm the server of the game, I should send an update for _all_players in the game
2331 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2332 for(idx=0;idx<MAX_PLAYERS;idx++){
2333 // only send info for connected players
2334 if(MULTI_CONNECTED(Net_players[idx])){
2339 // add the net player's information
2340 ADD_DATA(Net_players[idx].player_id);
2341 ADD_DATA(Net_players[idx].state);
2342 ADD_DATA(Net_players[idx].p_info.ship_class);
2343 ADD_DATA(Net_players[idx].tracker_player_id);
2345 if(Net_players[idx].flags & NETINFO_FLAG_HAS_CD){
2353 // add the final stop byte
2357 // broadcast the packet
2358 if(!(Game_mode & GM_IN_MISSION)){
2360 multi_io_send_to_all_reliable(data, packet_size);
2362 multi_io_send_reliable(pl, data, packet_size);
2366 multi_io_send_to_all(data, packet_size);
2368 multi_io_send(pl, data, packet_size);
2376 // add my current state in the netgame to this packet
2377 ADD_DATA(Net_player->player_id);
2378 ADD_DATA(Net_player->state);
2379 ADD_DATA(Net_player->p_info.ship_class);
2380 ADD_DATA(Multi_tracker_id);
2382 // add if I have a CD or not
2390 // add a final stop byte
2394 // send the packet to the server
2395 Assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
2396 if(!(Game_mode & GM_IN_MISSION)){
2397 multi_io_send_reliable(Net_player, data, packet_size);
2399 multi_io_send(Net_player, data, packet_size);
2404 // process an incoming netplayer state update. if we're the server, we should rebroadcast
2405 void process_netplayer_update_packet( ubyte *data, header *hinfo )
2407 int offset, player_num;
2413 offset = HEADER_LENGTH;
2415 // get the first stop byte
2418 while(stop != 0xff){
2419 // look the player up
2420 GET_DATA(player_id);
2421 player_num = find_player_id(player_id);
2422 // if we couldn't find him, read in the bogus data
2423 if((player_num == -1) || (Net_player == &Net_players[player_num])){
2424 GET_DATA(bogus.state);
2425 GET_DATA(bogus.p_info.ship_class);
2426 GET_DATA(bogus.tracker_player_id);
2430 // otherwise read in the data correctly
2432 GET_DATA(new_state);
2433 GET_DATA(Net_players[player_num].p_info.ship_class);
2434 GET_DATA(Net_players[player_num].tracker_player_id);
2437 Net_players[player_num].flags |= NETINFO_FLAG_HAS_CD;
2439 Net_players[player_num].flags &= ~(NETINFO_FLAG_HAS_CD);
2442 // if he's changing state to joined, send a team update
2443 if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
2444 multi_team_send_update();
2448 Net_players[player_num].state = new_state;
2451 // get the next stop byte
2457 // if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
2458 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2459 send_netplayer_update_packet(NULL);
2462 // if i'm the standalone and this is an update from the host, maybe change some netgame settings
2463 if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
2464 switch(Net_players[player_num].state){
2465 case NETPLAYER_STATE_STD_HOST_SETUP:
2466 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2469 case NETPLAYER_STATE_HOST_SETUP:
2470 // check for race conditions
2471 if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
2472 Netgame.game_state = NETGAME_STATE_FORMING;
2479 #define EXTRA_DEATH_VAPORIZED (1<<0)
2480 #define EXTRA_DEATH_WASHED (1<<1)
2481 // send a packet indicating a ship has been killed
2482 void send_ship_kill_packet( object *objp, object *other_objp, float percent_killed, int self_destruct )
2484 int packet_size, model;
2485 ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
2486 ushort debris_signature;
2490 // only sendable from the master
2491 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
2494 vaporized = ( (Ships[objp->instance].flags & SF_VAPORIZE) > 0 );
2496 extra_death_info = 0;
2498 extra_death_info |= EXTRA_DEATH_VAPORIZED;
2501 if ( Ships[objp->instance].wash_killed ) {
2502 extra_death_info |= EXTRA_DEATH_WASHED;
2505 // find out the next network signature that will be used for the debris pieces.
2506 model = Ships[objp->instance].modelnum;
2507 pm = model_get(model);
2508 debris_signature = 0;
2509 if ( pm && !vaporized ) {
2510 debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
2511 multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
2512 Ships[objp->instance].arrival_distance = debris_signature;
2515 BUILD_HEADER(SHIP_KILL);
2516 ADD_DATA(objp->net_signature);
2518 // ships which are initially killed get the rest of the data sent. self destructed ships and
2519 if ( other_objp == NULL ) {
2524 nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
2526 ADD_DATA( other_objp->net_signature );
2529 ADD_DATA( debris_signature );
2530 ADD_DATA( percent_killed );
2531 sd = (ubyte)self_destruct;
2533 ADD_DATA( extra_death_info );
2535 // if the ship who died is a player, then send some extra info, like who killed him, etc.
2537 if ( objp->flags & OF_PLAYER_SHIP ) {
2541 pnum = multi_find_player_by_object( objp );
2544 ADD_DATA( was_player );
2546 Assert(Net_players[pnum].player->killer_objtype < CHAR_MAX);
2547 temp = (char)Net_players[pnum].player->killer_objtype;
2550 Assert(Net_players[pnum].player->killer_species < CHAR_MAX);
2551 temp = (char)Net_players[pnum].player->killer_species;
2554 Assert(Net_players[pnum].player->killer_weapon_index < CHAR_MAX);
2555 temp = (char)Net_players[pnum].player->killer_weapon_index;
2558 ADD_STRING( Net_players[pnum].player->killer_parent_name );
2560 ADD_DATA( was_player );
2563 ADD_DATA( was_player );
2566 // send the packet reliably!!!
2567 multi_io_send_to_all_reliable(data, packet_size);
2570 // process a packet indicating that a ship has been killed
2571 void process_ship_kill_packet( ubyte *data, header *hinfo )
2574 ushort ship_sig, other_sig, debris_sig;
2575 object *sobjp, *oobjp;
2576 float percent_killed;
2577 ubyte was_player, extra_death_info, sd;
2578 char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = SPECIES_TERRAN, killer_weapon_index = -1;
2580 offset = HEADER_LENGTH;
2583 GET_DATA( other_sig );
2584 GET_DATA( debris_sig );
2585 GET_DATA( percent_killed );
2587 GET_DATA( extra_death_info );
2588 GET_DATA( was_player );
2591 // pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
2592 if ( was_player != 0 ) {
2593 GET_DATA( killer_objtype );
2594 GET_DATA( killer_species );
2595 GET_DATA( killer_weapon_index );
2596 GET_STRING( killer_name );
2601 sobjp = multi_get_network_object( ship_sig );
2603 // if I am unable to find the ship object which was killed, I have to bail and rely on getting
2604 // another message from the server that this happened!
2605 if ( sobjp == NULL ) {
2606 nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
2610 // set this ship's hull value to 0
2611 sobjp->hull_strength = 0.0f;
2613 // maybe set vaporized
2614 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2615 Ships[sobjp->instance].flags |= SF_VAPORIZE;
2618 // maybe set wash_killed
2619 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2620 Ships[sobjp->instance].wash_killed = 1;
2623 oobjp = multi_get_network_object( other_sig );
2625 if ( was_player != 0 ) {
2628 pnum = multi_find_player_by_object( sobjp );
2630 Net_players[pnum].player->killer_objtype = killer_objtype;
2631 Net_players[pnum].player->killer_species = killer_species;
2632 Net_players[pnum].player->killer_weapon_index = killer_weapon_index;
2633 strcpy( Net_players[pnum].player->killer_parent_name, killer_name );
2637 // check to see if I need to respawn myself
2638 multi_respawn_check(sobjp);
2640 // store the debris signature in the arrival distance which will never get used for player ships
2641 Ships[sobjp->instance].arrival_distance = debris_sig;
2643 // set this bit so that we don't accidentally start switching targets when we die
2644 if(sobjp == Player_obj){
2645 Game_mode |= GM_DEAD_DIED;
2648 nprintf(("Network", "Killing off %s\n", Ships[sobjp->instance].ship_name));
2650 // do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
2651 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2652 ship_hit_kill( sobjp, oobjp, percent_killed, sd );
2654 extern void ship_destroyed( int shipnum );
2655 ship_destroyed( sobjp->instance );
2656 sobjp->flags |= OF_SHOULD_BE_DEAD;
2657 obj_delete( OBJ_INDEX(sobjp) );
2661 // send a packet indicating a ship should be created
2662 void send_ship_create_packet( object *objp, int is_support )
2665 ubyte data[MAX_PACKET_SIZE];
2667 // We will pass the ship to create by name.
2668 BUILD_HEADER(SHIP_CREATE);
2669 ADD_DATA(objp->net_signature);
2670 ADD_DATA( is_support );
2672 ADD_DATA( objp->pos );
2675 // broadcast the packet
2676 multi_io_send_to_all_reliable(data, packet_size);
2679 // process a packet indicating a ship should be created
2680 void process_ship_create_packet( ubyte *data, header *hinfo )
2682 int offset, objnum, is_support;
2685 vector pos = ZERO_VECTOR;
2687 Assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
2688 offset = HEADER_LENGTH;
2689 GET_DATA(signature);
2690 GET_DATA( is_support );
2697 // find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
2698 if ( !is_support ) {
2699 objp = mission_parse_get_arrival_ship( signature );
2700 if ( objp != NULL ) {
2701 objnum = parse_create_object(objp);
2703 nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
2706 Assert( Arriving_support_ship );
2707 if(Arriving_support_ship == NULL){
2710 Arriving_support_ship->pos = pos;
2711 Arriving_support_ship->net_signature = signature;
2712 objnum = parse_create_object( Arriving_support_ship );
2713 Assert( objnum != -1 );
2715 mission_parse_support_arrived( objnum );
2720 // send a packet indicating a wing of ships should be created
2721 void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
2723 int packet_size, index, ship_instance;
2724 ubyte data[MAX_PACKET_SIZE];
2728 // for creating wing -- we just send the index into the wing array of this wing.
2729 // all players load the same mission, and so their array's should all match. We also
2730 // need to send the signature of the first ship that was created. We can find this by
2731 // looking num_to_create places back in the ship_index field in the wing structure.
2733 index = WING_INDEX(wingp);
2734 ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
2735 signature = Objects[Ships[ship_instance].objnum].net_signature;
2737 BUILD_HEADER( WING_CREATE );
2739 ADD_DATA(num_to_create);
2740 ADD_DATA(signature);
2741 ADD_DATA(pre_create_count);
2742 val = wingp->current_wave - 1;
2745 multi_io_send_to_all_reliable(data, packet_size);
2748 // process a packet saying that a wing should be created
2749 void process_wing_create_packet( ubyte *data, header *hinfo )
2751 int offset, index, num_to_create;
2753 int total_arrived_count, current_wave;
2755 offset = HEADER_LENGTH;
2757 GET_DATA(num_to_create);
2758 GET_DATA(signature);
2759 GET_DATA(total_arrived_count);
2760 GET_DATA(current_wave);
2764 // do a sanity check on the wing to be sure that we are actually working on a valid wing
2765 if ( (index < 0) || (index >= num_wings) || (Wings[index].num_waves == -1) ) {
2766 nprintf(("Network", "invalid index %d for wing create packet\n"));
2769 if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
2770 nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
2775 Wings[index].current_count = 0;
2776 Wings[index].total_arrived_count = total_arrived_count;
2777 Wings[index].current_wave = current_wave;
2779 // set the network signature that was passed. The client should create ships in the same order
2780 // as the server -- so all ships should get the same sigs as assigned by the server. We also
2781 // need to set some timestamps and cues correctly to be sure that these things get created on
2782 // the clients correctly
2783 multi_set_network_signature( signature, MULTI_SIG_SHIP );
2784 parse_wing_create_ships( &Wings[index], num_to_create, 1 );
2787 // packet indicating a ship is departing
2788 void send_ship_depart_packet( object *objp )
2790 ubyte data[MAX_PACKET_SIZE];
2794 signature = objp->net_signature;
2796 BUILD_HEADER(SHIP_DEPART);
2797 ADD_DATA( signature );
2799 multi_io_send_to_all_reliable(data, packet_size);
2802 // process a packet indicating a ship is departing
2803 void process_ship_depart_packet( ubyte *data, header *hinfo )
2809 offset = HEADER_LENGTH;
2810 GET_DATA( signature );
2813 // find the object which is departing
2814 objp = multi_get_network_object( signature );
2815 if ( objp == NULL ) {
2816 nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
2820 // start warping him out
2821 shipfx_warpout_start( objp );
2824 // packet to tell clients cargo of a ship was revealed to all
2825 void send_cargo_revealed_packet( ship *shipp )
2827 ubyte data[MAX_PACKET_SIZE];
2830 // build the header and add the data
2831 BUILD_HEADER(CARGO_REVEALED);
2832 ADD_DATA( Objects[shipp->objnum].net_signature );
2834 // server sends to all players
2835 if(MULTIPLAYER_MASTER){
2836 multi_io_send_to_all_reliable(data, packet_size);
2838 // clients just send to the server
2840 multi_io_send_reliable(Net_player, data, packet_size);
2844 // process a cargo revealed packet
2845 void process_cargo_revealed_packet( ubyte *data, header *hinfo )
2851 offset = HEADER_LENGTH;
2852 GET_DATA(signature);
2855 // get a ship pointer and call the ship function to reveal the cargo
2856 objp = multi_get_network_object( signature );
2857 if ( objp == NULL ) {
2858 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
2862 // Assert( objp->type == OBJ_SHIP );
2863 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2867 // this will take care of re-routing to all other clients
2868 ship_do_cargo_revealed( &Ships[objp->instance], 1);
2870 // server should rebroadcast
2871 if(MULTIPLAYER_MASTER){
2872 send_cargo_revealed_packet(&Ships[objp->instance]);
2876 // defines used for secondary fire packet
2877 #define SFPF_ALLOW_SWARM (1<<7)
2878 #define SFPF_DUAL_FIRE (1<<6)
2879 #define SFPF_TARGET_LOCKED (1<<5)
2881 // send a packet indicating a secondary weapon was fired
2882 void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm )
2884 int packet_size, net_player_num;
2885 ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
2887 ushort target_signature;
2891 // Assert ( starting_count < UCHAR_MAX );
2893 // get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
2894 // we might send the info to the other player different than the one who fired
2895 objp = &Objects[shipp->objnum];
2896 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
2897 if ( num_fired == 0 ) {
2902 aip = &Ai_info[shipp->ai_index];
2904 current_bank = (ubyte)shipp->weapons.current_secondary_bank;
2905 Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) );
2907 // build up the header portion
2908 BUILD_HEADER( SECONDARY_FIRED_AI );
2910 ADD_DATA( Objects[shipp->objnum].net_signature );
2911 ADD_DATA( starting_sig );
2913 // add a couple of bits for swarm missiles and dual fire secondary weaspons
2916 sinfo = current_bank;
2919 sinfo |= SFPF_ALLOW_SWARM;
2922 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ){
2923 sinfo |= SFPF_DUAL_FIRE;
2926 if ( aip->current_target_is_locked ){
2927 sinfo |= SFPF_TARGET_LOCKED;
2932 // add the ship's target and any targeted subsystem
2933 target_signature = 0;
2935 if ( aip->target_objnum != -1) {
2936 target_signature = Objects[aip->target_objnum].net_signature;
2937 if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
2940 s_index = ship_get_index_from_subsys( aip->targeted_subsys, aip->target_objnum );
2941 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
2942 t_subsys = (char)s_index;
2945 if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
2946 Assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags & WIF_BOMB);
2951 ADD_DATA( target_signature );
2952 ADD_DATA( t_subsys );
2954 // just send this packet to everyone, then bail if an AI ship fired.
2955 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
2956 multi_io_send_to_all(data, packet_size);
2960 net_player_num = multi_find_player_by_object( objp );
2962 // getting here means a player fired. Send the current packet to all players except the player
2963 // who fired. If nothing got fired, then don't send to the other players -- we will just send
2964 // a packet to the player who will find out that he didn't fire anything
2965 if ( num_fired > 0 ) {
2966 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
2969 // if I (the master) fired, then return
2970 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
2974 // now build up the packet to send to the player who actually fired.
2975 BUILD_HEADER( SECONDARY_FIRED_PLR );
2976 ADD_DATA(starting_sig);
2979 // add the targeting information so that the player's weapons will always home on the correct
2981 ADD_DATA( target_signature );
2982 ADD_DATA( t_subsys );
2984 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
2987 /// process a packet indicating a secondary weapon was fired
2988 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
2990 int offset, allow_swarm, target_objnum_save;
2991 ushort net_signature, starting_sig, target_signature;
2992 ubyte sinfo, current_bank;
2993 object* objp, *target_objp;
2997 ship_subsys *targeted_subsys_save;
2999 offset = HEADER_LENGTH; // size of the header
3001 // if from_player is false, it means that the secondary weapon info in this packet was
3002 // fired by an ai object (or another player). from_player == 1 means tha me (the person
3003 // receiving this packet) fired the secondary weapon
3004 if ( !from_player ) {
3005 GET_DATA( net_signature );
3006 GET_DATA( starting_sig );
3007 GET_DATA( sinfo ); // are we firing swarm missiles
3009 GET_DATA( target_signature );
3010 GET_DATA( t_subsys );
3014 // find the object (based on network signatures) for the object that fired
3015 objp = multi_get_network_object( net_signature );
3016 if ( objp == NULL ) {
3017 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3021 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3022 // of the next fire time of this bank to 0 so we can fire right away
3023 shipp = &Ships[objp->instance];
3026 GET_DATA( starting_sig );
3029 GET_DATA( target_signature );
3030 GET_DATA( t_subsys );
3034 // get the object and ship
3036 shipp = Player_ship;
3039 // check the allow swarm bit
3041 if ( sinfo & SFPF_ALLOW_SWARM ){
3045 // set the dual fire properties of the ship
3046 if ( sinfo & SFPF_DUAL_FIRE ){
3047 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3049 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3052 // determine whether current target is locked
3053 Assert( shipp->ai_index != -1 );
3054 aip = &Ai_info[shipp->ai_index];
3055 if ( sinfo & SFPF_TARGET_LOCKED ) {
3056 aip->current_target_is_locked = 1;
3058 aip->current_target_is_locked = 0;
3061 // find out the current bank
3062 current_bank = (ubyte)(sinfo & 0x3);
3063 Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) );
3064 shipp->weapons.current_secondary_bank = current_bank;
3066 // make it so we can fire this ship's secondary bank immediately!!!
3067 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3068 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3070 // set this ship's target and subsystem information. We will save and restore target and
3071 // targeted subsystem so that we do not accidentally change targets for this player or
3072 // any AI ships on his system.
3073 target_objnum_save = aip->target_objnum;
3074 targeted_subsys_save = aip->targeted_subsys;
3076 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3077 aip->target_objnum = -1;
3078 aip->targeted_subsys = NULL;
3080 target_objp = multi_get_network_object( target_signature );
3081 if ( target_objp != NULL ) {
3082 aip->target_objnum = OBJ_INDEX(target_objp);
3084 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3085 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3089 if ( starting_sig != 0 ){
3090 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3092 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3095 ship_fire_secondary( objp, allow_swarm );
3097 // restore targeted object and targeted subsystem
3098 aip->target_objnum = target_objnum_save;
3099 aip->targeted_subsys = targeted_subsys_save;
3102 // send a packet indicating a countermeasure was fired
3103 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3105 ubyte data[MAX_PACKET_SIZE];
3110 Assert ( cmeasure_count < UCHAR_MAX );
3111 BUILD_HEADER(COUNTERMEASURE_FIRED);
3112 ADD_DATA( objp->net_signature );
3113 ADD_DATA( rand_val );
3115 multi_io_send_to_all(data, packet_size);
3118 // process a packet indicating a countermeasure was fired
3119 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3121 int offset, rand_val;
3127 offset = HEADER_LENGTH;
3129 GET_DATA( signature );
3130 GET_DATA( rand_val );
3133 objp = multi_get_network_object( signature );
3134 if ( objp == NULL ) {
3135 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3138 if(objp->type != OBJ_SHIP){
3141 // Assert ( objp->type == OBJ_SHIP );
3143 // make it so ship can fire right away!
3144 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3145 if ( objp == Player_obj ){
3146 nprintf(("network", "firing countermeasure from my ship\n"));
3149 ship_launch_countermeasure( objp, rand_val );
3152 // send a packet indicating that a turret has been fired
3153 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3156 ushort pnet_signature;
3157 ubyte data[MAX_PACKET_SIZE], cindex;
3164 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3168 // local setup -- be sure we are actually passing a weapon!!!!
3169 objp = &Objects[weapon_objnum];
3170 Assert ( objp->type == OBJ_WEAPON );
3171 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3175 pnet_signature = Objects[ship_objnum].net_signature;
3177 Assert( subsys_index < UCHAR_MAX );
3178 cindex = (ubyte)subsys_index;
3180 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3185 // build the fire turret packet.
3186 BUILD_HEADER(FIRE_TURRET_WEAPON);
3187 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.fvec);
3188 ADD_DATA( has_sig );
3189 ADD_DATA( pnet_signature );
3191 ADD_DATA( objp->net_signature );
3194 val = (short)ssp->submodel_info_1.angs.h;
3196 val = (short)ssp->submodel_info_2.angs.p;
3199 multi_io_send_to_all(data, packet_size);
3201 multi_rate_add(1, "tur", packet_size);
3204 // process a packet indicating a turret has been fired
3205 void process_turret_fired_packet( ubyte *data, header *hinfo )
3207 int offset, weapon_objnum, wid;
3208 ushort pnet_signature, wnet_signature;
3217 short pitch, heading;
3219 // get the data for the turret fired packet
3220 offset = HEADER_LENGTH;
3221 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3222 GET_DATA( has_sig );
3223 GET_DATA( pnet_signature );
3225 GET_DATA( wnet_signature );
3229 GET_DATA( turret_index );
3230 GET_DATA( heading );
3232 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3235 objp = multi_get_network_object( pnet_signature );
3236 if ( objp == NULL ) {
3237 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3241 // if this isn't a ship, do nothing
3242 if ( objp->type != OBJ_SHIP ){
3246 // make an orientation matrix from the o_fvec
3247 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3249 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3250 // hack, but should be suitable.
3251 shipp = &Ships[objp->instance];
3252 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3256 wid = ssp->system_info->turret_weapon_type;
3258 // bash the position and orientation of the turret
3259 ssp->submodel_info_1.angs.h = (float)heading;
3260 ssp->submodel_info_2.angs.p = (float)pitch;
3262 // get the world position of the weapon
3263 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3265 // create the weapon object
3266 if(wnet_signature != 0){
3267 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3269 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3270 if (weapon_objnum != -1) {
3271 if ( Weapon_info[wid].launch_snd != -1 ) {
3272 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3277 // send a mission log item packet
3278 void send_mission_log_packet( int num )
3281 ubyte data[MAX_PACKET_SIZE];
3286 Assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3288 // get the data from the log
3289 entry = &log_entries[num];
3290 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3291 sindex = (ushort)entry->index;
3293 BUILD_HEADER(MISSION_LOG_ENTRY);
3295 ADD_DATA(entry->flags);
3297 ADD_DATA(entry->timestamp);
3298 ADD_STRING(entry->pname);
3299 ADD_STRING(entry->sname);
3301 // broadcast the packet to all players
3302 multi_io_send_to_all_reliable(data, packet_size);
3305 // process a mission log item packet
3306 void process_mission_log_packet( ubyte *data, header *hinfo )
3311 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3314 Assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3316 offset = HEADER_LENGTH;
3320 GET_DATA(timestamp);
3326 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3329 // send a mission message packet
3330 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)
3333 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3335 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3336 Assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3337 Assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3339 up = (ubyte) priority;
3340 us = (ubyte) source;
3341 utime = (ubyte)timing;
3343 BUILD_HEADER(MISSION_MESSAGE);
3345 ADD_STRING(who_from);
3349 ADD_DATA(builtin_type);
3350 ADD_DATA(multi_team_filter);
3352 if (multi_target == -1){
3353 multi_io_send_to_all_reliable(data, packet_size);
3355 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3359 // process a mission message packet
3360 void process_mission_message_packet( ubyte *data, header *hinfo )
3362 int offset, id, builtin_type;
3363 ubyte priority, source, utiming;
3364 char who_from[NAME_LENGTH];
3365 int multi_team_filter;
3367 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3369 offset = HEADER_LENGTH;
3371 GET_STRING(who_from);
3375 GET_DATA(builtin_type);
3376 GET_DATA(multi_team_filter);
3380 // filter out builtin ones in TvT
3381 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3385 // maybe filter this out
3386 if(!message_filter_multi(id)){
3387 // send the message as if it came from an sexpression
3388 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3392 // just send them a pong back as fast as possible
3393 void process_ping_packet(ubyte *data, header *hinfo)
3398 offset = HEADER_LENGTH;
3401 // get the address to return the pong to
3402 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3408 // right now it just routes the pong through to the standalone gui, which is the only
3409 // system which uses ping and pong right now.
3410 void process_pong_packet(ubyte *data, header *hinfo)
3416 offset = HEADER_LENGTH;
3418 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3422 // if we're connected , see who sent us this pong
3423 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3424 lookup = find_player_id(hinfo->id);
3429 p = &Net_players[lookup];
3431 // evaluate the ping
3432 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3434 // put in calls to any functions which may want to know about the ping times from
3436 if(Game_mode & GM_STANDALONE_SERVER){
3437 std_update_player_ping(p);
3440 // mark his socket as still alive (extra precaution)
3441 psnet_mark_received(Net_players[lookup].reliable_socket);
3443 // otherwise, do any special processing
3445 // if we're in the join game state, see if this pong came from a server on our
3447 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3448 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3453 // send a ping packet
3454 void send_ping(net_addr *addr)
3456 unsigned char data[8];
3459 // build the header and send the packet
3460 BUILD_HEADER( PING );
3461 psnet_send(addr, &data[0], packet_size);
3464 // send a pong packet
3465 void send_pong(net_addr *addr)
3467 unsigned char data[8];
3470 // build the header and send the packet
3472 psnet_send(addr, &data[0], packet_size);
3475 // sent from host to master. give me the list of missions you have.
3476 // this will be used only in a standalone mode
3477 void send_mission_list_request( int what )
3479 ubyte data[MAX_PACKET_SIZE];
3482 // build the header and ask for a list of missions or campaigns (depending
3483 // on the 'what' flag).
3484 BUILD_HEADER(MISSION_REQUEST);
3486 multi_io_send_reliable(Net_player, data, packet_size);
3489 // maximum number of bytes that we can send in a mission items packet.
3490 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3492 // defines used to tell what type of packets are being sent
3493 #define MISSION_LIST_ITEMS 1
3494 #define CAMPAIGN_LIST_ITEMS 2
3496 // send an individual mission file item
3497 void send_mission_items( net_player *pl )
3499 ubyte data[MAX_PACKET_SIZE];
3504 BUILD_HEADER(MISSION_ITEM);
3506 // send the list of missions and campaigns avilable on the server. Stop when
3507 // reaching a certain maximum
3508 type = MISSION_LIST_ITEMS;
3510 for (i = 0; i < Multi_create_mission_count; i++ ) {
3514 ADD_STRING( Multi_create_mission_list[i].filename );
3515 ADD_STRING( Multi_create_mission_list[i].name );
3516 ADD_DATA( Multi_create_mission_list[i].flags );
3517 ADD_DATA( Multi_create_mission_list[i].max_players );
3518 ADD_DATA( Multi_create_mission_list[i].respawn );
3521 ADD_DATA( Multi_create_mission_list[i].valid_status );
3523 if ( packet_size > MAX_MISSION_ITEMS_BYTES ) {
3526 multi_io_send_reliable(pl, data, packet_size);
3527 BUILD_HEADER( MISSION_ITEM );
3533 multi_io_send_reliable(pl, data, packet_size);
3535 // send the campaign information
3536 type = CAMPAIGN_LIST_ITEMS;
3537 BUILD_HEADER(MISSION_ITEM);
3539 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3543 ADD_STRING( Multi_create_campaign_list[i].filename );
3544 ADD_STRING( Multi_create_campaign_list[i].name );
3545 ADD_DATA( Multi_create_campaign_list[i].flags );
3546 ADD_DATA( Multi_create_campaign_list[i].max_players );
3548 if ( packet_size > MAX_MISSION_ITEMS_BYTES ) {
3551 multi_io_send_reliable(pl, data, packet_size);
3552 BUILD_HEADER( MISSION_ITEM );
3558 multi_io_send_reliable(pl, data, packet_size);
3561 // process a request for a list of missions
3562 void process_mission_request_packet(ubyte *data, header *hinfo)
3564 int player_num,offset;
3566 offset = HEADER_LENGTH;
3569 // fill in the address information of where this came from
3570 player_num = find_player_id(hinfo->id);
3571 if(player_num == -1){
3572 nprintf(("Network","Could not find player to send mission list items to!\n"));
3576 send_mission_items( &Net_players[player_num] );
3579 // process an individual mission file item
3580 void process_mission_item_packet(ubyte *data,header *hinfo)
3583 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3584 ubyte stop, type,max_players;
3587 Assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3588 offset = HEADER_LENGTH;
3593 GET_STRING( filename );
3596 GET_DATA( max_players );
3598 // missions also have respawns and a crc32 associated with them
3599 if(type == MISSION_LIST_ITEMS){
3603 GET_DATA(valid_status);
3605 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3606 strcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename );
3607 strcpy(Multi_create_mission_list[Multi_create_mission_count].name, name );
3608 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3609 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3610 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3613 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3615 Multi_create_mission_count++;
3617 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3618 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3619 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename );
3620 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name );
3621 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3622 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3623 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3624 Multi_create_campaign_count++;
3633 // this will cause whatever list to get resorted (although they should be appearing in order)
3634 multi_create_setup_list_data(-1);
3637 // send a request to the server to pause or unpause the game
3638 void send_multi_pause_packet(int pause)
3640 ubyte data[MAX_PACKET_SIZE];
3642 int packet_size = 0;
3644 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3647 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3648 val = (ubyte) pause;
3650 // add the pause info
3653 // send the request to the server
3654 multi_io_send_reliable(Net_player, data, packet_size);
3657 // process a pause update packet (pause, unpause, etc)
3658 void process_multi_pause_packet(ubyte *data, header *hinfo)
3664 offset = HEADER_LENGTH;
3670 // get who sent the packet
3671 player_index = find_player_id(hinfo->id);
3672 // if we don't know who sent the packet, don't do anything
3673 if(player_index == -1){
3677 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3678 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3681 // send a game information update
3682 void send_game_info_packet()
3685 ubyte data[MAX_PACKET_SIZE], paused;
3687 // set the paused variable
3688 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3690 BUILD_HEADER(GAME_INFO);
3691 ADD_DATA( Missiontime );
3694 multi_io_send_to_all(data, packet_size);
3697 // process a game information update
3698 void process_game_info_packet( ubyte *data, header *hinfo )
3704 offset = HEADER_LENGTH;
3706 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3707 // time, set our time to server time (should take ping time into account!!!)
3708 GET_DATA( mission_time );
3713 // send an ingame nak packet
3714 void send_ingame_nak(int state, net_player *p)
3716 ubyte data[MAX_PACKET_SIZE];
3719 BUILD_HEADER(INGAME_NAK);
3723 multi_io_send_reliable(p, data, packet_size);
3726 // process an ingame nak packet
3727 void process_ingame_nak(ubyte *data, header *hinfo)
3729 int offset,state,pid;
3732 offset = HEADER_LENGTH;
3736 pid = find_player_id(hinfo->id);
3740 pl = &Net_players[pid];
3743 case ACK_FILE_ACCEPTED :
3744 Assert(Net_player->flags & NETINFO_FLAG_INGAME_JOIN);
3745 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3746 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3751 // send a packet telling players to end the mission
3752 void send_endgame_packet(net_player *pl)
3754 ubyte data[MAX_PACKET_SIZE];
3758 BUILD_HEADER(MISSION_END);
3760 // sending to a specific player?
3762 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3763 multi_io_send_reliable(pl, data, packet_size);
3767 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3768 // send all player stats here
3769 multi_broadcast_stats(STATS_MISSION);
3771 // if in dogfight mode, send all dogfight stats as well
3772 ml_string("Before dogfight stats!");
3773 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3774 ml_string("Sending dogfight stats!");
3776 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3778 ml_string("After dogfight stats!");
3780 // tell everyone to leave the game
3781 multi_io_send_to_all_reliable(data, packet_size);
3783 multi_io_send_reliable(Net_player, data, packet_size);
3787 // process a packet indicating we should end the current mission
3788 void process_endgame_packet(ubyte *data, header *hinfo)
3793 offset = HEADER_LENGTH;
3797 ml_string("Receiving endgame packet");
3799 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3800 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3801 // determine who this came from and make sure he is allowed to end the game
3802 player_num = find_player_id(hinfo->id);
3803 Assert(player_num != -1);
3808 // if the player is allowed to end the mission
3809 if(!multi_can_end_mission(&Net_players[player_num])){
3813 // act as if we hit alt+j locally
3814 multi_handle_end_mission_request();
3816 // all clients process immediately
3818 // ingame joiners should quit when they receive an endgame packet since the game is over
3819 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3820 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3824 // do any special processing for being in a state other than the gameplay state
3825 multi_handle_state_special();
3827 // make sure we're not already in the debrief state
3828 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3829 multi_warpout_all_players();
3834 // send a position/orientation update for myself (if I'm an observer)
3835 void send_observer_update_packet()
3837 ubyte data[MAX_PACKET_SIZE];
3842 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3843 // to send any update packets to anyone.
3844 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3848 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3854 BUILD_HEADER(OBSERVER_UPDATE);
3856 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3858 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3861 // add targeting infomation
3862 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3863 target_sig = Objects[Player_ai->target_objnum].net_signature;
3867 ADD_DATA(target_sig);
3869 multi_io_send(Net_player, data, packet_size);
3872 // process a position/orientation update from an observer
3873 void process_observer_update_packet(ubyte *data, header *hinfo)
3879 physics_info bogus_pi;
3882 offset = HEADER_LENGTH;
3884 obs_num = find_player_id(hinfo->id);
3886 memset(&bogus_pi,0,sizeof(physics_info));
3887 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
3889 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
3892 // targeting information
3893 GET_DATA(target_sig);
3896 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
3900 // set targeting info
3901 if(target_sig == 0){
3902 Net_players[obs_num].s_info.target_objnum = -1;
3904 target_obj = multi_get_network_object(target_sig);
3905 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
3908 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
3909 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
3910 Net_players[obs_num].s_info.eye_pos = g_vec;
3911 Net_players[obs_num].s_info.eye_orient = g_mat;
3914 void send_netplayer_slot_packet()
3916 ubyte data[MAX_PACKET_SIZE];
3917 int packet_size,idx;
3922 BUILD_HEADER(NETPLAYER_SLOTS_P);
3923 for(idx=0;idx<MAX_PLAYERS;idx++){
3924 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
3926 ADD_DATA(Net_players[idx].player_id);
3927 ADD_DATA(Objects[Net_players[idx].player->objnum].net_signature);
3928 ADD_DATA(Net_players[idx].p_info.ship_class);
3929 ADD_DATA(Net_players[idx].p_info.ship_index);
3935 // standalone case or not
3936 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3937 multi_io_send_to_all_reliable(data, packet_size);
3939 multi_io_send_reliable(Net_player, data, packet_size);
3943 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
3946 int player_num,ship_class,ship_index;
3952 offset = HEADER_LENGTH;
3954 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
3955 multi_untag_player_ships();
3959 GET_DATA(player_id);
3961 GET_DATA(ship_class);
3962 GET_DATA(ship_index);
3963 player_num = find_player_id(player_id);
3965 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
3967 // call the function in multiutil.cpp to set up the player object stuff
3968 // being careful not to muck with the standalone object
3969 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
3970 objp = multi_get_network_object(net_sig);
3971 Assert(objp != NULL);
3972 multi_assign_player_ship( player_num, objp, ship_class );
3973 Net_players[player_num].p_info.ship_index = ship_index;
3974 objp->flags &= ~(OF_COULD_BE_PLAYER);
3975 objp->flags |= OF_PLAYER_SHIP;
3982 // standalone should forward the packet and wait for a response
3983 if(Game_mode & GM_STANDALONE_SERVER){
3984 send_netplayer_slot_packet();
3987 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
3988 send_netplayer_update_packet();
3991 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
3992 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
3993 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
3994 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
3996 ubyte data[MAX_PACKET_SIZE], utmp;
3999 BUILD_HEADER(SHIP_WSTATE_CHANGE);
4000 ADD_DATA( Objects[shipp->objnum].net_signature );
4001 utmp = (ubyte)(what);
4003 utmp = (ubyte)(new_bank);
4005 utmp = (ubyte)(link_status);
4008 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
4009 multi_io_send_to_all_reliable(data, packet_size);
4012 void process_ship_weapon_change( ubyte *data, header *hinfo )
4016 ubyte what, new_bank, link_status;
4020 offset = HEADER_LENGTH;
4021 GET_DATA( signature );
4023 GET_DATA( new_bank );
4024 GET_DATA( link_status );
4027 objp = multi_get_network_object( signature );
4028 if ( objp == NULL ) {
4029 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4032 // Assert( objp->type == OBJ_SHIP );
4033 if(objp->type != OBJ_SHIP){
4037 // if this is my data, do nothing since I already have my own data
4038 if ( objp == Player_obj ){
4042 // now, get the ship and set the new bank and link modes based on the 'what' value
4043 shipp = &Ships[objp->instance];
4044 if ( what == MULTI_PRIMARY_CHANGED ) {
4045 shipp->weapons.current_primary_bank = new_bank;
4047 shipp->flags |= SF_PRIMARY_LINKED;
4049 shipp->flags &= ~SF_PRIMARY_LINKED;
4052 shipp->weapons.current_secondary_bank = new_bank;
4054 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4056 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4061 // ship status change procedure
4062 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4064 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4065 // for added redundancy.
4066 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4067 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4068 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4069 // back to the client. Also sends multiple times for redundancy
4070 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4071 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4072 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4074 #define SHIP_STATUS_REPEAT 2
4075 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4078 ubyte data[MAX_PACKET_SIZE];
4079 int packet_size = 0;
4085 BUILD_HEADER(SHIP_STATUS_CHANGE);
4087 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4088 temp = bi->status[idx];
4092 // server should send reliably (response packet)
4093 if(MULTIPLAYER_MASTER){
4094 multi_io_send_reliable(pl, data, packet_size);
4096 multi_io_send(pl, data, packet_size);
4100 void process_ship_status_packet(ubyte *data, header *hinfo)
4104 int player_num,unique_id;
4108 offset = HEADER_LENGTH;
4110 // zero out the button info structure for good measure
4111 memset(&bi,0,sizeof(button_info));
4113 // read the button-info
4114 GET_DATA(unique_id);
4116 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4118 bi.status[idx] = i_tmp;
4123 // this will be handled differently client and server side. Duh.
4124 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4125 // find which net-player has sent us butotn information
4126 player_num = find_player_id(hinfo->id);
4127 Assert(player_num >= 0);
4132 // don't process critical button information for observers
4133 // its a new button_info for this guy. apply and ack
4134 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4135 // mark that he's pressed this button
4136 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4138 // send a return packet
4139 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4141 // apply the button presses to his ship as normal
4142 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4144 // else ignore it as a repeat from the same guy
4145 } else { // CLIENT SIDE
4146 // this is the return from the server, so we should now apply them locally
4147 // if(lookup_ship_status(Net_player,unique_id,1)){
4148 multi_apply_ship_status(Net_player, &bi, 1);
4153 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4154 // for clients. Left all details to this function instead of higher level messaging
4156 void send_player_order_packet(int type, int index, int cmd)
4158 ubyte data[MAX_PACKET_SIZE];
4160 ushort target_signature;
4162 int packet_size = 0;
4164 BUILD_HEADER(PLAYER_ORDER_PACKET);
4167 ADD_DATA(val); // ship order or wing order, or message all fighters
4169 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4170 if ( val != SQUAD_MSG_ALL ){
4171 ADD_DATA(index); // net signature of target ship
4174 ADD_DATA(cmd); // the command itself
4177 target_signature = 0;
4178 if ( Player_ai->target_objnum != -1 ){
4179 target_signature = Objects[Player_ai->target_objnum].net_signature;
4182 ADD_DATA( target_signature );
4185 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4188 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4189 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
4190 t_subsys = (char)s_index;
4194 multi_io_send_reliable(Net_player, data, packet_size);
4197 // brief explanation :
4198 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4199 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4200 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4201 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4202 void process_player_order_packet(ubyte *data, header *hinfo)
4204 int offset, player_num, command, index = 0, tobjnum_save;
4205 ushort target_signature;
4206 char t_subsys, type;
4207 object *objp, *target_objp;
4210 ship_subsys *tsubsys_save, *targeted_subsys;
4212 Assert(MULTIPLAYER_MASTER);
4214 // packet values - its easier to read all of these in first
4216 offset = HEADER_LENGTH;
4219 if ( type != SQUAD_MSG_ALL ){
4223 GET_DATA( command );
4224 GET_DATA( target_signature );
4225 GET_DATA( t_subsys );
4229 player_num = find_player_id(hinfo->id);
4230 if(player_num == -1){
4231 nprintf(("Network","Received player order packet from unknown player\n"));
4235 objp = &Objects[Net_players[player_num].player->objnum];
4236 if ( objp->type != OBJ_SHIP ) {
4237 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4241 // HACK HACK HACK HACK HACK HACK
4242 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4243 // the data is BOGUS. All people should be able to to these things as well.
4244 if(command == REARM_REPAIR_ME_ITEM){
4245 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4247 } else if(command == ABORT_REARM_REPAIR_ITEM){
4248 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4252 // if this player is not allowed to do messaging, quit here
4253 if( !multi_can_message(&Net_players[player_num]) ){
4254 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4258 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4259 // then call them in.
4260 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4261 Assert( (index >= 0) && (index < Num_reinforcements) );
4262 hud_squadmsg_call_reinforcement(index, player_num);
4266 // set the player's ai information here
4267 shipp = &Ships[objp->instance];
4268 aip = &Ai_info[shipp->ai_index];
4270 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4271 target_objp = multi_get_network_object( target_signature );
4272 if ( target_objp == NULL ) {
4276 targeted_subsys = NULL;
4277 if ( t_subsys != -1 ) {
4278 Assert( target_objp != NULL );
4279 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4282 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4284 tobjnum_save = aip->target_objnum;
4285 tsubsys_save = aip->targeted_subsys;
4287 if ( target_objp ) {
4288 aip->target_objnum = OBJ_INDEX(target_objp);
4290 aip->target_objnum = -1;
4293 aip->targeted_subsys = targeted_subsys;
4295 if ( type == SQUAD_MSG_SHIP ) {
4296 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4297 } else if ( type == SQUAD_MSG_WING ) {
4298 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4299 } else if ( type == SQUAD_MSG_ALL ) {
4300 hud_squadmsg_send_to_all_fighters( command, player_num );
4303 Assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4304 aip->target_objnum = tobjnum_save;
4305 aip->targeted_subsys = tsubsys_save;
4308 // FILE SIGNATURE stuff :
4309 // there are 2 cases for file signature sending which are handled very differently
4310 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4311 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4312 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4313 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4314 // would have to keep track of up to potentially 14 other ack handles (ouch).
4315 void send_file_sig_packet(ushort sum_sig,int length_sig)
4317 ubyte data[MAX_PACKET_SIZE];
4318 int packet_size = 0;
4320 BUILD_HEADER(FILE_SIG_INFO);
4322 ADD_DATA(length_sig);
4324 multi_io_send_reliable(Net_player, data, packet_size);
4327 void process_file_sig_packet(ubyte *data, header *hinfo)
4332 offset = HEADER_LENGTH;
4334 // should only be received on the server-side
4335 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4338 GET_DATA(length_sig);
4340 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4343 void send_file_sig_request(char *file_name)
4345 ubyte data[MAX_PACKET_SIZE];
4346 int packet_size = 0;
4348 BUILD_HEADER(FILE_SIG_REQUEST);
4349 ADD_STRING(file_name);
4351 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4353 multi_io_send_to_all_reliable(data, packet_size);
4356 void process_file_sig_request(ubyte *data, header *hinfo)
4358 int offset = HEADER_LENGTH;
4360 // get the mission name
4361 GET_STRING(Netgame.mission_name);
4364 // set the current mission filename
4365 strcpy(Game_current_mission_filename,Netgame.mission_name);
4368 multi_get_mission_checksum(Game_current_mission_filename);
4370 if(!multi_endgame_ending()){
4371 // reply to the server
4372 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4376 // functions to deal with subsystems getting whacked
4377 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4379 ubyte data[MAX_PACKET_SIZE];
4382 vector tmp, local_hitpos;
4385 Assert ( index < UCHAR_MAX );
4386 uindex = (ubyte)(index);
4388 objp = &Objects[shipp->objnum];
4390 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4391 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4393 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4394 ADD_DATA( Objects[shipp->objnum].net_signature );
4396 ADD_DATA( local_hitpos );
4398 multi_io_send_to_all_reliable(data, packet_size);
4401 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4407 vector local_hit_pos, world_hit_pos;
4409 offset = HEADER_LENGTH;
4411 GET_DATA( signature );
4413 GET_DATA( local_hit_pos );
4415 // get the network object. process it if we find it.
4416 objp = multi_get_network_object( signature );
4417 if ( objp != NULL ) {
4419 ship_subsys *subsysp;
4421 // be sure we have a ship!!!
4422 // Assert ( objp->type == OBJ_SHIP );
4423 if(objp->type != OBJ_SHIP){
4428 shipp = &Ships[objp->instance];
4430 // call to get the pointer to the subsystem we should be working on
4431 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4432 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4433 vm_vec_add2( &world_hit_pos, &objp->pos );
4435 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4436 if ( objp == Player_obj ) {
4437 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4445 // packet to tell clients cargo of a ship was revealed to all
4446 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4448 ubyte data[MAX_PACKET_SIZE], uindex;
4451 Assert ( index < UCHAR_MAX );
4452 uindex = (ubyte)(index);
4454 // build the header and add the data
4455 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4456 ADD_DATA( Objects[shipp->objnum].net_signature );
4459 // server sends to all players
4460 if(MULTIPLAYER_MASTER){
4461 multi_io_send_to_all_reliable(data, packet_size);
4463 // clients just send to the server
4465 multi_io_send_reliable(Net_player, data, packet_size);
4469 // process a subsystem cargo revealed packet
4470 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4477 ship_subsys *subsysp;
4479 offset = HEADER_LENGTH;
4480 GET_DATA( signature );
4484 // get a ship pointer and call the ship function to reveal the cargo
4485 objp = multi_get_network_object( signature );
4486 if ( objp == NULL ) {
4487 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4491 // Assert( objp->type == OBJ_SHIP );
4492 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4496 shipp = &Ships[objp->instance];
4498 // call to get the pointer to the subsystem we should be working on
4499 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4500 if (subsysp == NULL) {
4501 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4505 // this will take care of re-routing to all other clients
4506 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4507 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4509 // server should rebroadcast
4510 if(MULTIPLAYER_MASTER){
4511 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4515 void send_netplayer_load_packet(net_player *pl)
4517 ubyte data[MAX_PACKET_SIZE];
4518 int packet_size = 0;
4520 BUILD_HEADER(LOAD_MISSION_NOW);
4521 ADD_STRING(Netgame.mission_name);
4524 multi_io_send_to_all_reliable(data, packet_size);
4526 multi_io_send_reliable(pl, data, packet_size);
4530 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4533 int offset = HEADER_LENGTH;
4538 strcpy(Netgame.mission_name,str);
4539 strcpy(Game_current_mission_filename,str);
4540 if(!Multi_mission_loaded){
4542 // MWA 2/3/98 -- ingame join changes!!!
4543 // everyone can go through the same mission loading path here!!!!
4544 nprintf(("Network","Loading mission..."));
4546 // notify everyone that I'm loading the mission
4547 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4548 send_netplayer_update_packet();
4550 // do the load itself
4551 game_start_mission();
4553 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4554 // code will remark the correct player ships
4555 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4556 multi_untag_player_ships();
4559 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4560 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4561 send_netplayer_update_packet();
4563 Multi_mission_loaded = 1;
4564 nprintf(("Network","Finished loading mission\n"));
4568 void send_jump_into_mission_packet(net_player *pl)
4570 ubyte data[MAX_PACKET_SIZE];
4571 int packet_size = 0;
4573 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4575 BUILD_HEADER(JUMP_INTO_GAME);
4577 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4578 // and possible other things.
4580 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4581 ADD_DATA(Netgame.game_state);
4587 multi_io_send_to_all_reliable(data, packet_size);
4589 // send to a specific player
4591 multi_io_send_reliable(pl, data, packet_size);
4595 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4597 int offset = HEADER_LENGTH;
4602 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4603 // the game could be paused, so ingame joiner needs to deal with it.
4604 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4606 Netgame.game_state = state;
4611 // handle any special processing for being in a weird substate
4612 multi_handle_state_special();
4614 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4615 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4616 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4617 multi_ingame_observer_finish();
4619 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4620 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4621 send_netplayer_update_packet();
4624 // start the mission!!
4625 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4626 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4627 gameseq_post_event(GS_EVENT_ENTER_GAME);
4628 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4629 send_netplayer_update_packet();
4633 extern int Player_multi_died_check;
4634 Player_multi_died_check = -1;
4636 // recalc all object pairs now
4637 extern void obj_reset_all_collisions();
4638 obj_reset_all_collisions();
4640 // display some cool text
4641 multi_common_add_text(XSTR("Received mission start\n",720),1);
4644 ml_string(NOX("Client received mission start from server - entering mission"));
4649 char *repair_text[] = {
4651 "REPAIR_INFO_BEGIN",
4653 "REPAIR_INFO_UPDATE",
4654 "REPAIR_INFO_QUEUE",
4655 "REPAIR_INFO_ABORT",
4656 "REPAIR_INFO_BROKEN",
4657 "REPAIR_INFO_WARP_ADD",
4658 "REPAIR_INFO_WARP_REMOVE",
4659 "REPAIR_INFO_ONWAY",
4660 "REPAIR_INFO_KILLED",
4661 "REPAIR_INFO_COMPLETE",
4666 // the following two routines deal with updating and sending information regarding players
4667 // rearming and repairing during the game. The process function calls the routines to deal with
4668 // setting flags and other interesting things.
4669 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4671 int packet_size = 0;
4672 ushort repaired_signature, repair_signature;
4673 ubyte data[MAX_PACKET_SIZE];
4676 // use the network signature of the destination object if there is one, -1 otherwise.
4677 // client will piece it all together
4678 repaired_signature = repaired_objp->net_signature;
4680 // the repair ship may be NULL here since it might have been destroyed
4681 repair_signature = 0;
4683 repair_signature = repair_objp->net_signature;
4686 BUILD_HEADER(CLIENT_REPAIR_INFO);
4689 ADD_DATA( repaired_signature );
4690 ADD_DATA( repair_signature );
4692 multi_io_send_to_all_reliable(data, packet_size);
4694 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));
4697 void process_repair_info_packet(ubyte *data, header *hinfo)
4699 int offset = HEADER_LENGTH;
4700 ushort repaired_signature, repair_signature;
4701 object *repaired_objp, *repair_objp;
4705 GET_DATA( repaired_signature );
4706 GET_DATA( repair_signature );
4709 repaired_objp = multi_get_network_object( repaired_signature );
4710 repair_objp = multi_get_network_object( repair_signature );
4712 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));
4714 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4718 if ( repaired_objp == NULL ) {
4719 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4723 // the hope is to simply call the routine in the ai code to set/unset flags
4724 // based on the code value and everything else should happen..I hope....
4725 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4727 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4729 // set the dock flags when repair begins. Prevents problem in lagging docking
4730 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4732 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4733 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4734 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4737 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4739 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4740 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4743 if ( code == REPAIR_INFO_WARP_ADD ){
4744 mission_warp_in_support_ship( repaired_objp );
4746 mission_remove_scheduled_repair( repaired_objp );
4751 // sends information updating clients on certain AI information that clients will
4752 // need to know about to keep HUD information up to date. objp is the object that we
4753 // are updating, and what is the type of stuff that we are updating.
4754 void send_ai_info_update_packet( object *objp, char what )
4757 ushort other_signature;
4758 ubyte data[MAX_PACKET_SIZE];
4760 ubyte dock_index, dockee_index;
4762 // Assert( objp->type == OBJ_SHIP );
4763 if(objp->type != OBJ_SHIP){
4766 aip = &Ai_info[Ships[objp->instance].ai_index];
4769 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4772 BUILD_HEADER( AI_INFO_UPDATE );
4773 ADD_DATA( objp->net_signature );
4776 // depending on the "what" value, we will send different information
4780 case AI_UPDATE_DOCK:
4781 // for docking ships, add the signature of the ship that we are docking with.
4782 Assert( aip->dock_objnum != -1 );
4783 other_signature = Objects[aip->dock_objnum].net_signature;
4784 dock_index = (ubyte)(aip->dock_index);
4785 dockee_index = (ubyte)(aip->dockee_index);
4786 ADD_DATA( other_signature );
4787 ADD_DATA(dock_index);
4788 ADD_DATA(dockee_index);
4791 case AI_UPDATE_UNDOCK:
4792 // for undocking ships, check the dock_objnum since we might or might not have it
4793 // depending on whether or not a ship was destroyed while we were docked.
4794 other_signature = 0;
4795 if ( aip->dock_objnum != -1 )
4796 other_signature = Objects[aip->dock_objnum].net_signature;
4797 ADD_DATA( other_signature );
4801 case AI_UPDATE_ORDERS: {
4804 // for orders, we only need to send a little bit of information here. Be sure that the
4805 // first order for this ship is active
4806 Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4807 ADD_DATA( aip->goals[0].ai_mode );
4808 ADD_DATA( aip->goals[0].ai_submode );
4810 if ( aip->goals[0].ship_name != NULL )
4811 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4813 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4814 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4815 if ( shipnum != -1 ) {
4816 Assert( Ships[shipnum].objnum != -1 );
4817 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4819 other_signature = 0;
4821 ADD_DATA( other_signature );
4823 // for docking, add the dock and dockee index
4824 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4825 Assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4826 Assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4827 dock_index = (ubyte)aip->goals[0].docker.index;
4828 dockee_index = (ubyte)aip->goals[0].dockee.index;
4829 ADD_DATA( dock_index );
4830 ADD_DATA( dockee_index );
4839 multi_rate_add(1, "aiu", packet_size);
4840 multi_io_send_to_all_reliable(data, packet_size);
4843 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4844 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4845 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4847 int offset = HEADER_LENGTH;
4849 ushort net_signature, other_net_signature;
4850 object *objp, *other_objp;
4853 ubyte dock_index = 0, dockee_index = 0;
4855 GET_DATA( net_signature ); // signature of the object that we are dealing with.
4856 GET_DATA( code ); // code of what we are doing.
4857 objp = multi_get_network_object( net_signature );
4859 nprintf(("Network", "Couldn't find object for ai update\n"));
4862 case AI_UPDATE_DOCK:
4863 GET_DATA( other_net_signature );
4864 GET_DATA( dock_index );
4865 GET_DATA( dockee_index );
4866 other_objp = multi_get_network_object( other_net_signature );
4868 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
4870 // if we don't have an object to work with, break out of loop
4871 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
4875 Assert( other_objp->type == OBJ_SHIP );
4876 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
4877 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
4879 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4880 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4882 ai_do_objects_docked_stuff( objp, other_objp );
4885 case AI_UPDATE_UNDOCK:
4886 GET_DATA( other_net_signature );
4887 other_objp = multi_get_network_object( other_net_signature );
4889 // if we don't have an object to work with, break out of loop
4893 ai_do_objects_undocked_stuff( objp, other_objp );
4896 case AI_UPDATE_ORDERS:
4898 GET_DATA( submode );
4899 GET_DATA( other_net_signature );
4900 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4901 GET_DATA(dock_index);
4902 GET_DATA(dockee_index);
4905 // be sure that we have a ship object!!!
4906 if ( !objp || (objp->type != OBJ_SHIP) )
4909 // set up the information in the first goal element of the object in question
4910 aip = &Ai_info[Ships[objp->instance].ai_index];
4911 aip->active_goal = 0;
4912 aip->goals[0].ai_mode = mode;
4913 aip->goals[0].ai_submode = submode;
4915 // for docking, add the dock and dockee index
4916 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4917 aip->dock_index = dock_index;
4918 aip->dockee_index = dockee_index;
4921 // get a shipname if we can.
4922 other_objp = multi_get_network_object( other_net_signature );
4923 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
4924 // get a pointer to the shipname in question. Use the ship_name value in the
4925 // ship. We are only using this for HUD display, so I think that using this
4926 // method will be fine.
4927 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
4929 // special case for destroy subsystem -- get the ai_info pointer to our target ship
4930 // so that we can properly set up what subsystem this ship is attacking.
4931 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
4932 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
4934 // if docking -- set the dock index and dockee index of this other ship
4935 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4936 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4937 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4944 Int3(); // this Int3() should be temporary
4945 nprintf(("Network", "Invalid code for ai update: %d\n", code));
4951 // tell the standalone to move into the MISSION_SYNC_STATE
4952 void send_mission_sync_packet(int mode,int start_campaign)
4954 ubyte data[MAX_PACKET_SIZE],is_campaign;
4955 int packet_size = 0;
4957 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
4959 // build the header and add the sync mode (pre or post briefing)
4960 BUILD_HEADER(MISSION_SYNC_DATA);
4963 // if this is a campaign game
4964 if(mode == MULTI_SYNC_PRE_BRIEFING){
4965 if(Game_mode & GM_CAMPAIGN_MODE){
4966 // add a byte indicating campaign mode
4968 ADD_DATA(is_campaign);
4970 // add a byte indicating if we should be starting a campaign or continuing it
4971 is_campaign = (ubyte)start_campaign;
4972 ADD_DATA(is_campaign);
4974 // add the campaign filename
4975 ADD_STRING(Netgame.campaign_name);
4977 // otherwise if this is a single mission
4979 // add a byte indicating single mission mode
4981 ADD_DATA(is_campaign);
4983 // add the mission filename
4984 ADD_STRING(Game_current_mission_filename);
4987 multi_io_send_reliable(Net_player, data, packet_size);
4990 // move into the MISSION_SYNC state when this is received
4991 // this packet is sent only from a game host to a standalone
4992 void process_mission_sync_packet(ubyte *data, header *hinfo)
4995 ubyte campaign_flag;
4996 int offset = HEADER_LENGTH;
4998 Assert(Game_mode & GM_STANDALONE_SERVER);
5000 // if this is a team vs team situation, lock the players send a final team update
5001 if(Netgame.type_flags & NG_TYPE_TEAM){
5002 multi_team_host_lock_all();
5003 multi_team_send_update();
5006 // get the sync mode (pre or post briefing)
5009 if(mode == MULTI_SYNC_PRE_BRIEFING){
5010 // get the flag indicating if this is a single mission or a campaign mode
5011 GET_DATA(campaign_flag);
5013 // get the flag indicating whether we should be starting a new campaign
5014 GET_DATA(campaign_flag);
5016 // get the campaign filename
5017 GET_STRING(Netgame.campaign_name);
5019 // either start a new campaign or continue on to the next mission in the current campaign
5021 multi_campaign_start(Netgame.campaign_name);
5023 multi_campaign_next_mission();
5026 // make sure we remove the campaign mode flag
5027 Game_mode &= ~(GM_CAMPAIGN_MODE);
5029 // get the single mission filename
5030 GET_STRING(Game_current_mission_filename);
5031 strcpy(Netgame.mission_name,Game_current_mission_filename);
5036 // set the correct mode and m ove into the state
5037 Multi_sync_mode = mode;
5038 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5041 // tell a player to merge his mission stats into his alltime stats
5042 void send_store_stats_packet(int accept)
5045 int packet_size = 0;
5047 BUILD_HEADER(STORE_MISSION_STATS);
5049 // add whether we're accepting or tossing
5050 val = (ubyte)accept;
5053 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5054 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5055 multi_io_send_to_all_reliable(data, packet_size);
5057 multi_io_send_reliable(Net_player, data, packet_size);
5061 void process_store_stats_packet(ubyte *data, header *hinfo)
5063 int offset = HEADER_LENGTH;
5069 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5070 if(Game_mode & GM_STANDALONE_SERVER){
5071 // rebroadcast the packet to all others in the game
5072 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5073 multi_io_send_to_all_reliable(data, offset);
5076 // all players should mark the stats as being accepted in the debriefing
5077 multi_debrief_stats_accept();
5079 // all players should mark the stats as being "tossed" in the debriefing
5080 multi_debrief_stats_toss();
5085 void send_debris_update_packet(object *objp,int code)
5087 ubyte data[MAX_PACKET_SIZE];
5089 int packet_size = 0;
5091 BUILD_HEADER(DEBRIS_UPDATE);
5092 ADD_DATA(objp->net_signature);
5096 // add any extra relevant data
5098 case DEBRIS_UPDATE_UPDATE:
5099 ADD_DATA(objp->pos); // add position
5100 ADD_ORIENT(objp->orient); // add orientation
5101 ADD_DATA(objp->phys_info.vel); // add velocity
5102 ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5105 multi_io_send_to_all(data, packet_size);
5108 void process_debris_update_packet(ubyte *data, header *hinfo)
5112 object bogus_object;
5114 int offset = HEADER_LENGTH;
5120 objp = multi_get_network_object(net_sig);
5122 objp = &bogus_object;
5126 // update the object
5127 case DEBRIS_UPDATE_UPDATE:
5128 GET_DATA(objp->pos);
5129 GET_ORIENT(objp->orient);
5130 GET_DATA(objp->phys_info.vel);
5131 GET_DATA(objp->phys_info.rotvel);
5133 // simply remove it (no explosion)
5134 case DEBRIS_UPDATE_REMOVE:
5135 if(objp != &bogus_object){
5136 Assert(objp->type == OBJ_DEBRIS);
5137 obj_delete(OBJ_INDEX(objp));
5141 case DEBRIS_UPDATE_NUKE:
5142 if(objp != &bogus_object)
5143 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5151 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)
5153 ubyte data[MAX_PACKET_SIZE];
5155 int packet_size = 0;
5157 BUILD_HEADER(WSS_REQUEST_PACKET);
5159 // add the request information
5160 ADD_DATA(player_id);
5161 ADD_DATA(from_slot);
5162 ADD_DATA(from_index);
5165 ADD_DATA(wl_ship_slot); // only used in weapons loadout
5166 ADD_DATA(ship_class);
5169 // a standard request
5171 multi_io_send_reliable(Net_player, data, packet_size);
5173 // being routed through the standalone to the host of the game
5175 Assert(Game_mode & GM_STANDALONE_SERVER);
5176 multi_io_send_reliable(p, data, packet_size);
5180 void process_wss_request_packet(ubyte *data, header *hinfo)
5182 int offset = HEADER_LENGTH;
5183 int from_slot,from_index;
5184 int to_slot,to_index;
5186 int wl_ship_slot,ship_class;
5190 // determine who this request is from
5191 GET_DATA(player_id);
5192 player_num = find_player_id(player_id);
5194 // read in the request data
5195 GET_DATA(from_slot);
5196 GET_DATA(from_index);
5199 GET_DATA(wl_ship_slot); // only used in weapons loadout
5200 GET_DATA(ship_class); // only used in multi team select
5204 Assert(player_num != -1);
5205 if(player_num == -1){
5209 // if we're the standalone, we have to route this packet to the host of the game
5210 if(Game_mode & GM_STANDALONE_SERVER){
5211 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5213 // otherwise we're the host and should process the request
5216 case WSS_WEAPON_SELECT :
5217 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5219 case WSS_SHIP_SELECT :
5220 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5228 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5230 ubyte data[MAX_PACKET_SIZE],team;
5231 int packet_size = 0;
5233 Assert(size <= (MAX_PACKET_SIZE - 10));
5235 BUILD_HEADER(WSS_UPDATE_PACKET);
5237 // add the team/pool # this is for
5238 team = (ubyte)team_num;
5241 // add the data block size
5244 // add the data itself
5245 memcpy(data + packet_size,wss_data,size);
5246 packet_size += size;
5248 // if we're also the master of the game (not on a standalone)
5249 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5250 multi_io_send_to_all_reliable(data, packet_size);
5252 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5254 multi_io_send_reliable(Net_player, data, packet_size);
5258 void process_wss_update_packet(ubyte *data, header *hinfo)
5261 int size,player_index,idx;
5262 int offset = HEADER_LENGTH;
5264 // get the team/pool #
5267 // get the data size
5270 // if we're the standalone, then we should be routing this data to all the other clients
5271 if(Game_mode & GM_STANDALONE_SERVER){
5276 // determine where this came from
5277 player_index = find_player_id(hinfo->id);
5278 Assert(player_index != -1);
5279 if(player_index < 0){
5283 // route the packet (don't resend it to the host)
5284 for(idx=0;idx<MAX_PLAYERS;idx++){
5285 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5286 multi_io_send_reliable(&Net_players[idx], data, offset);
5290 // set the proper pool pointers
5291 ss_set_team_pointers((int)team);
5293 // read in the block of data, and apply it to the weapons/ship pools
5294 offset += restore_wss_data(data + offset);
5297 // set the pool pointers back to my own team
5298 ss_set_team_pointers(Net_player->p_info.team);
5300 // sync the interface if this was for my own team
5301 if((int)team == Net_player->p_info.team){
5302 multi_ts_sync_interface();
5309 // function to send firing information from the client to the server once they reach
5310 // the final sync screen.
5311 void send_firing_info_packet()
5313 ubyte data[MAX_PACKET_SIZE];
5315 ubyte plinked, sdual;
5317 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5319 BUILD_HEADER(FIRING_INFO);
5320 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5321 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5322 ADD_DATA( plinked );
5325 multi_io_send_reliable(Net_player, data, packet_size);
5328 void process_firing_info_packet( ubyte *data, header *hinfo )
5330 int offset, player_num;
5331 ubyte plinked, sdual;
5334 // only the master of the game should be dealing with these packets
5335 Assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5337 offset = HEADER_LENGTH;
5338 GET_DATA( plinked );
5342 player_num = find_player_id(hinfo->id);
5344 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5348 // get the ship pointer for this player and set the flags accordingly.
5349 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5351 shipp->flags |= SF_PRIMARY_LINKED;
5353 shipp->flags &= ~SF_PRIMARY_LINKED;
5356 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5358 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5361 // packet to deal with changing status of mission goals. used to be sent every so often from server
5362 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5363 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5364 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5365 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5367 ubyte data[MAX_PACKET_SIZE];
5370 BUILD_HEADER(MISSION_GOAL_INFO);
5373 ADD_DATA(new_status);
5376 multi_io_send_to_all_reliable(data, packet_size);
5379 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5381 int offset, goal_num, new_status, valid;
5383 offset = HEADER_LENGTH;
5385 GET_DATA(new_status);
5389 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5390 if ( new_status != -1 ){
5391 mission_goal_status_change( goal_num, new_status );
5393 mission_goal_validation_change( goal_num, valid );
5397 void send_player_settings_packet(net_player *p)
5399 ubyte data[MAX_PACKET_SIZE];
5402 int packet_size = 0;
5405 BUILD_HEADER(PLAYER_SETTINGS);
5407 // add all the data for all the players
5409 for(idx=0;idx<MAX_PLAYERS;idx++){
5410 if(MULTI_CONNECTED(Net_players[idx])){
5412 ADD_DATA(Net_players[idx].player_id);
5414 // break the p_info structure by member, so we don't overwrite any absolute pointers
5415 // ADD_DATA(Net_players[idx].p_info);
5416 ADD_DATA(Net_players[idx].p_info.team);
5417 ADD_DATA(Net_players[idx].p_info.ship_index);
5418 ADD_DATA(Net_players[idx].p_info.ship_class);
5421 // add the stop byte
5425 // either broadcast the data or send to a specific player
5427 multi_io_send_to_all_reliable(data, packet_size);
5429 multi_io_send_reliable(p, data, packet_size);
5433 void process_player_settings_packet(ubyte *data, header *hinfo)
5435 int offset,player_num;
5436 net_player_info bogus,*ptr;
5440 offset = HEADER_LENGTH;
5442 // read in the data for all the players
5444 while(stop != 0xff){
5445 // lookup the player
5446 GET_DATA(player_id);
5447 player_num = find_player_id(player_id);
5449 // make sure this is a valid player
5450 if(player_num == -1){
5453 ptr = &Net_players[player_num].p_info;
5456 GET_DATA(ptr->team);
5457 GET_DATA(ptr->ship_index);
5458 GET_DATA(ptr->ship_class);
5465 // update the server with my new state
5466 // MWA -- 3/31/98 -- check for in mission instead of state.
5467 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5468 if( !(Game_mode & GM_IN_MISSION) ) {
5469 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5470 send_netplayer_update_packet();
5474 // display some cool text
5475 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5478 void send_deny_packet(net_addr *addr, int code)
5481 int packet_size = 0;
5483 // build the header and add the rejection code
5489 psnet_send(addr, data, packet_size);
5492 void process_deny_packet(ubyte *data, header *hinfo)
5496 // get the denial code
5497 offset = HEADER_LENGTH;
5501 // if there is already a dialog active, do nothing - who cares at this point.
5506 // display the appropriate dialog
5508 case JOIN_DENY_JR_STATE :
5509 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));
5511 case JOIN_DENY_JR_TRACKER_INVAL :
5512 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));
5514 case JOIN_DENY_JR_PASSWD :
5515 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5517 case JOIN_DENY_JR_CLOSED :
5518 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));
5520 case JOIN_DENY_JR_TEMP_CLOSED :
5521 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));
5523 case JOIN_DENY_JR_RANK_HIGH :
5524 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));
5526 case JOIN_DENY_JR_RANK_LOW :
5527 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a rank limited game and your rank is too low",728));
5529 case JOIN_DENY_JR_DUP :
5530 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5532 case JOIN_DENY_JR_FULL :
5533 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5535 case JOIN_DENY_JR_BANNED :
5536 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5538 case JOIN_DENY_JR_NOOBS :
5539 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5541 case JOIN_DENY_JR_INGAME_JOIN :
5542 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));
5544 case JOIN_DENY_JR_BAD_VERSION :
5545 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));
5547 case JOIN_DENY_JR_TYPE :
5548 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5552 // call this so that the join request timestamp automatically expires when we hear back from the server
5553 multi_join_reset_join_stamp();
5556 // this packet will consist of
5557 // 1.) netplayer ship classes (85 bytes max)
5558 // 2.) ship weapon state data (241 bytes max)
5559 // 3.) player settings et. al. (133 bytes max)
5560 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5561 void send_post_sync_data_packet(net_player *p, int std_request)
5563 ubyte data[MAX_PACKET_SIZE], val;
5568 ushort sval, ship_ets;
5569 int idx, player_index;
5570 int packet_size = 0;
5574 BUILD_HEADER(POST_SYNC_DATA);
5576 // some header information for standalone packet routing purposes
5577 val = (ubyte)std_request;
5580 // the standalone has two situations
5581 // 1.) sending a request to the host to distribute this block of data
5582 // 2.) having recevied this block of data from the host, it redistributes it
5583 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5584 // case 1, send the request
5585 multi_io_send_reliable(Netgame.host, data, packet_size);
5588 // case 2 for the standalone is below (as normal)
5590 // otherwise build the data now
5592 // add all deleted ships
5593 val = (ubyte)Multi_ts_num_deleted;
5595 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5596 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5602 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5603 shipp = &Ships[Objects[so->objnum].instance];
5605 // don't process non player wing ships
5606 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5612 // # of ships - used multiple times in the packet
5613 val = (ubyte)ship_count;
5616 // add ship class information (85 bytes max)
5617 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5618 shipp = &Ships[Objects[so->objnum].instance];
5620 // don't process non player wing ships
5621 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5624 // add the net signature of the object for look up
5625 ADD_DATA( Objects[so->objnum].net_signature );
5627 // add the ship info index
5628 val = (ubyte)(shipp->ship_info_index);
5631 // add the ships's team select index
5632 val = (ubyte)shipp->ts_index;
5636 // add weapon state information for all starting ships (241 bytes max)
5637 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5638 shipp = &Ships[Objects[so->objnum].instance];
5640 // don't process non player wing ships
5641 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5644 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5646 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5647 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5648 if(player_index == -1){
5651 pl = &Net_players[player_index];
5655 // add the net signature and other weapon information
5656 ADD_DATA( Objects[so->objnum].net_signature );
5658 // add number of primary and secondary banks
5659 bval = (char)(shipp->weapons.num_primary_banks);
5661 bval = (char)(shipp->weapons.num_secondary_banks);
5664 // add weapon bank status
5665 bval = (char)(shipp->weapons.current_primary_bank);
5667 pl->s_info.cur_primary_bank = bval;
5669 // Assert(bval != -1);
5672 bval = (char)(shipp->weapons.current_secondary_bank);
5674 pl->s_info.cur_secondary_bank = bval;
5676 // Assert(bval != -1);
5679 // primary weapon info
5680 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5682 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5685 // secondary weapon info
5686 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5688 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5689 ADD_DATA(val_short);
5690 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5692 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5693 ADD_DATA(val_short);
5694 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5696 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5697 ADD_DATA(val_short);
5699 // send primary and secondary weapon link status
5701 if(shipp->flags & SF_PRIMARY_LINKED){
5703 pl->s_info.cur_link_status |= (1<<0);
5707 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5709 pl->s_info.cur_link_status |= (1<<1);
5713 // if this is a player ship add (1<<2)
5714 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5719 // add a ship ets value
5722 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5724 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5726 ship_ets |= ((ushort)shipp->engine_recharge_index);
5731 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5732 // or if I'm the server as well as the host, I should be sending this to all players
5733 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5734 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5737 multi_io_send_to_all_reliable(data, packet_size);
5739 // send to a specific player
5741 multi_io_send_reliable(p, data, packet_size);
5744 multi_io_send_reliable(Net_player, data, packet_size);
5751 multi_io_send_to_all_reliable(data, packet_size);
5753 // send to a specific player
5755 multi_io_send_reliable(p, data, packet_size);
5760 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5762 ubyte val, sinfo_index, ts_index;
5764 ushort net_sig, ship_ets, sval;
5768 int offset = HEADER_LENGTH;
5772 // packet routing information
5775 // 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
5776 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5779 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5780 multi_ts_create_wings();
5782 // send to the standalone through my socket
5783 send_post_sync_data_packet(Net_player);
5789 // add all deleted ships
5791 Multi_ts_num_deleted = (int)val;
5792 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5793 // get the ship's objnum
5796 objp = multi_get_network_object(sval);
5798 // delete the ship appropriately
5799 // mark the object as having been deleted
5800 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5803 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5804 obj_delete(Multi_ts_deleted_objnums[idx]);
5805 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5807 Multi_ts_num_deleted--;
5808 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5816 // process ship class information
5817 for(idx=0; idx<ship_count; idx++){
5818 // get the object's net signature
5820 GET_DATA(sinfo_index);
5823 // attempt to get the object
5824 objp = multi_get_network_object(net_sig);
5826 // make sure we found a ship
5827 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5829 // set the ship to be the right class
5830 change_ship_type(objp->instance,(int)sinfo_index);
5832 // set the ship's team select index
5833 Ships[objp->instance].ts_index = (int)ts_index;
5836 // process ship weapon state info
5837 for(idx=0; idx<ship_count; idx++){
5838 // get the object's net signature
5841 // attempt to get the object
5842 objp = multi_get_network_object(net_sig);
5844 // make sure we found a ship
5845 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5847 // get a pointer to the ship
5848 shipp = &Ships[objp->instance];
5850 // get number of primary and secondary banks;
5853 shipp->weapons.num_primary_banks = (int)b;
5857 shipp->weapons.num_secondary_banks = (int)b;
5859 // get bank selection info
5864 shipp->weapons.current_primary_bank = (int)b;
5870 shipp->weapons.current_secondary_bank = (int)b;
5872 // primary weapon info
5874 shipp->weapons.primary_bank_weapons[0] = (int)b;
5877 shipp->weapons.primary_bank_weapons[1] = (int)b;
5879 // secondary weapon info
5881 shipp->weapons.secondary_bank_weapons[0] = (int)b;
5882 GET_DATA(val_short);
5883 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
5886 shipp->weapons.secondary_bank_weapons[1] = (int)b;
5887 GET_DATA(val_short);
5888 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
5891 shipp->weapons.secondary_bank_weapons[2] = (int)b;
5892 GET_DATA(val_short);
5893 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
5900 shipp->flags |= SF_PRIMARY_LINKED;
5903 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5905 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
5906 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
5908 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
5910 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
5916 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
5918 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
5920 shipp->engine_recharge_index = (ship_ets & 0x000f);
5925 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
5926 send_netplayer_update_packet();
5928 // the standalone server will receive this packet from the host of the game, to be applied locally and
5929 // also to be rebroadcast.
5930 if(Game_mode & GM_STANDALONE_SERVER){
5931 // update player ets settings
5932 for(idx=0;idx<MAX_PLAYERS;idx++){
5933 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
5934 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
5938 send_post_sync_data_packet(NULL,0);
5942 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
5944 ubyte data[MAX_PACKET_SIZE],val;
5947 int packet_size = 0;
5950 BUILD_HEADER(WSS_SLOTS_DATA);
5952 // some header information for standalone packet routing purposes
5953 val = (ubyte)std_request;
5957 val = (ubyte)team_num;
5960 // add whether this is the final packet or not
5964 // the standalone has two situations
5965 // 1.) sending a request to the host to distribute this block of data
5966 // 2.) having recevied this block of data from the host, it redistributes it
5967 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
5968 // case 1, send the request
5969 multi_io_send_reliable(Netgame.host, data, packet_size);
5972 // case 2 for the standalone is below (as normal)
5974 // add all the slots
5975 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
5976 // add the ship class
5977 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
5981 for(i = 0;i<MAX_WL_WEAPONS;i++){
5982 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
5986 // add the weapon counts
5987 for(i = 0;i<MAX_WL_WEAPONS;i++){
5988 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
5989 ADD_DATA(val_short);
5993 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5994 // or if I'm the server as well as the host, I should be sending this to all players
5995 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5996 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5999 multi_io_send_to_all_reliable(data, packet_size);
6001 // send to a specific player
6003 multi_io_send_reliable(p, data, packet_size);
6006 multi_io_send_reliable(Net_player, data, packet_size);
6013 multi_io_send_to_all_reliable(data, packet_size);
6015 // send to a specific player
6017 multi_io_send_reliable(p, data, packet_size);
6022 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6024 ubyte val,team_num,final;
6026 int offset = HEADER_LENGTH;
6029 // packet routing information
6035 // get whether this is the final packet or not
6038 // 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
6039 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6042 // send to the standalone through my socket
6043 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6047 // read in all the slot data
6048 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6049 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6051 // get the ship class
6053 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6056 for(i = 0;i<MAX_WL_WEAPONS;i++){
6058 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6060 // check for signed/unsigned problems
6061 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6062 Wss_slots_teams[team_num][idx].wep[i] = -1;
6066 // get the weapon counts
6067 for(i = 0;i<MAX_WL_WEAPONS;i++){
6068 GET_DATA(val_short);
6069 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6074 // update my netplayer state if this is the final packet
6076 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6077 send_netplayer_update_packet();
6080 // the standalone server will receive this packet from the host of the game, to be applied locally and
6081 // also to be rebroadcast.
6082 if(Game_mode & GM_STANDALONE_SERVER){
6083 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6085 // add some mission sync text
6086 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6090 #define OBJ_VISIBILITY_DOT 0.6f
6092 // send and receive packets for shield explosion information
6093 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6096 ubyte data[MAX_PACKET_SIZE], utri_num;
6099 // Assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6101 Assert( tri_num < UCHAR_MAX );
6102 utri_num = (ubyte)tri_num;
6104 // for each player, determine if this object is behind the player -- if so, don't
6106 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6107 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6109 vector eye_to_obj_vec, diff, eye_pos;
6112 eye_pos = Net_players[i].s_info.eye_pos;
6113 eye_orient = Net_players[i].s_info.eye_orient;
6115 // check for same vectors
6116 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6117 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6121 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6122 dot = vm_vec_dot(&eye_orient.fvec, &eye_to_obj_vec);
6124 if ( dot < OBJ_VISIBILITY_DOT ){
6128 BUILD_HEADER(SHIELD_EXPLOSION);
6130 ADD_DATA( Objects[objnum].net_signature );
6133 multi_io_send(&Net_players[i], data, packet_size);
6138 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6140 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6147 // get the shield hit data
6148 offset = HEADER_LENGTH;
6149 GET_DATA(signature);
6151 //GET_DATA(hit_pos);
6154 // find the object with this signature. If found, then do a shield explosion
6155 objp = multi_get_network_object( signature );
6158 shield_info *shieldp;
6163 // given the tri num, find the local position which is the average of the
6164 // three vertices of the triangle affected. Use this average point as the hit
6166 // Assert( objp->type == OBJ_SHIP );
6167 if(objp->type != OBJ_SHIP){
6171 pm = model_get(Ships[objp->instance].modelnum);
6172 shieldp = &pm->shield;
6173 Assert( utri_num < shieldp->ntris );
6174 stri = shieldp->tris[utri_num];
6175 vm_vec_zero(&hit_pos);
6176 for ( i = 0; i < 3; i++ ) {
6177 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6179 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6180 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6184 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6187 ubyte data[MAX_PACKET_SIZE], val;
6189 int packet_size = 0;
6194 sc = &pl->player->stats;
6197 BUILD_HEADER(PLAYER_STATS);
6199 // add the player id
6200 ADD_DATA(pl->player_id);
6202 // add the byte indicating whether these stats are all-time or not
6203 val = (ubyte)stats_code;
6206 // kill information - alltime
6210 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6211 u_tmp = sc->kills[idx];
6214 // medal information
6215 for(idx=0;idx<NUM_MEDALS;idx++){
6216 i_tmp = sc->medals[idx];
6220 ADD_DATA(sc->score);
6222 ADD_DATA(sc->assists);
6223 ADD_DATA(sc->kill_count);
6224 ADD_DATA(sc->kill_count_ok);
6225 ADD_DATA(sc->p_shots_fired);
6226 ADD_DATA(sc->s_shots_fired);
6227 ADD_DATA(sc->p_shots_hit);
6228 ADD_DATA(sc->s_shots_hit);
6229 ADD_DATA(sc->p_bonehead_hits);
6230 ADD_DATA(sc->s_bonehead_hits);
6231 ADD_DATA(sc->bonehead_kills);
6233 ADD_DATA(sc->missions_flown);
6234 ADD_DATA(sc->flight_time);
6235 ADD_DATA(sc->last_flown);
6236 ADD_DATA(sc->last_backup);
6241 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6242 u_tmp = sc->m_okKills[idx];
6246 ADD_DATA(sc->m_score);
6247 ADD_DATA(sc->m_assists);
6248 ADD_DATA(sc->m_kill_count);
6249 ADD_DATA(sc->m_kill_count_ok);
6250 ADD_DATA(sc->mp_shots_fired);
6251 ADD_DATA(sc->ms_shots_fired);
6252 ADD_DATA(sc->mp_shots_hit);
6253 ADD_DATA(sc->ms_shots_hit);
6254 ADD_DATA(sc->mp_bonehead_hits);
6255 ADD_DATA(sc->ms_bonehead_hits);
6256 ADD_DATA(sc->m_bonehead_kills);
6257 ADD_DATA(sc->m_player_deaths);
6258 ADD_DATA(sc->m_medal_earned);
6261 case STATS_MISSION_KILLS:
6262 ADD_DATA(sc->m_kill_count);
6263 ADD_DATA(sc->m_kill_count_ok);
6264 ADD_DATA(sc->m_assists);
6267 case STATS_DOGFIGHT_KILLS:
6268 for(idx=0; idx<MAX_PLAYERS; idx++){
6269 u_tmp = sc->m_dogfight_kills[idx];
6272 ADD_DATA(sc->m_kill_count);
6273 ADD_DATA(sc->m_kill_count_ok);
6274 ADD_DATA(sc->m_assists);
6278 Assert(packet_size < MAX_PACKET_SIZE);
6280 // if we're a client, always send the data to the server
6281 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6282 multi_io_send_reliable(Net_player, data, packet_size);
6284 // otherwise do server specific stuff
6286 // send to a specific target
6288 multi_io_send_reliable(target, data, packet_size);
6290 // otherwise, send to everyone
6292 multi_io_send_to_all_reliable(data, packet_size);
6297 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6301 scoring_struct *sc,bogus;
6303 int offset = HEADER_LENGTH;
6307 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6309 // get the player who these stats are for
6310 GET_DATA(player_id);
6311 player_num = find_player_id(player_id);
6312 if (player_num == -1) {
6313 nprintf(("Network", "Couldn't find player for stats update!\n"));
6314 ml_string("Couldn't find player for stats update!\n");
6319 sc = &Net_players[player_num].player->stats;
6322 // get the stats code
6326 ml_string("Received STATS_ALLTIME\n");
6329 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6331 sc->kills[idx] = u_tmp;
6334 // read in the stats
6335 for (idx=0; idx<NUM_MEDALS; idx++) {
6337 sc->medals[idx] = i_tmp;
6340 GET_DATA(sc->score);
6342 GET_DATA(sc->assists);
6343 GET_DATA(sc->kill_count);
6344 GET_DATA(sc->kill_count_ok);
6345 GET_DATA(sc->p_shots_fired);
6346 GET_DATA(sc->s_shots_fired);
6347 GET_DATA(sc->p_shots_hit);
6348 GET_DATA(sc->s_shots_hit);
6349 GET_DATA(sc->p_bonehead_hits);
6350 GET_DATA(sc->s_bonehead_hits);
6351 GET_DATA(sc->bonehead_kills);
6353 GET_DATA(sc->missions_flown);
6354 GET_DATA(sc->flight_time);
6355 GET_DATA(sc->last_flown);
6356 GET_DATA(sc->last_backup);
6360 ml_string("Received STATS_MISSION\n");
6362 // kills - mission OK
6363 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6365 sc->m_okKills[idx] = u_tmp;
6368 GET_DATA(sc->m_score);
6369 GET_DATA(sc->m_assists);
6370 GET_DATA(sc->m_kill_count);
6371 GET_DATA(sc->m_kill_count_ok);
6372 GET_DATA(sc->mp_shots_fired);
6373 GET_DATA(sc->ms_shots_fired);
6374 GET_DATA(sc->mp_shots_hit);
6375 GET_DATA(sc->ms_shots_hit);
6376 GET_DATA(sc->mp_bonehead_hits);
6377 GET_DATA(sc->ms_bonehead_hits);
6378 GET_DATA(sc->m_bonehead_kills);
6379 GET_DATA(sc->m_player_deaths);
6380 GET_DATA(sc->m_medal_earned);
6383 case STATS_MISSION_KILLS:
6384 ml_string("Received STATS_MISSION_KILLS\n");
6386 GET_DATA(sc->m_kill_count);
6387 GET_DATA(sc->m_kill_count_ok);
6388 GET_DATA(sc->m_assists);
6391 case STATS_DOGFIGHT_KILLS:
6392 ml_string("Received STATS_DOGFIGHT_KILLS\n");
6393 if(player_num >= 0){
6394 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6396 for(idx=0; idx<MAX_PLAYERS; idx++){
6398 sc->m_dogfight_kills[idx] = u_tmp;
6399 if(player_num >= 0){
6400 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6403 GET_DATA(sc->m_kill_count);
6404 GET_DATA(sc->m_kill_count_ok);
6405 GET_DATA(sc->m_assists);
6410 // if I'm the server of the game, I should always rebroadcast these stats
6411 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6412 // make sure these are alltime stats
6413 Assert(val == STATS_ALLTIME);
6415 multi_broadcast_stats(STATS_ALLTIME);
6419 // called to create asteroid stuff
6420 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6423 ubyte data[MAX_PACKET_SIZE];
6424 ubyte packet_type, atype;
6428 if (relvec != NULL )
6431 BUILD_HEADER( ASTEROID_INFO );
6432 packet_type = ASTEROID_CREATE;
6434 Assert( asteroid_type < UCHAR_MAX );
6435 atype = (ubyte)asteroid_type;
6437 ADD_DATA( packet_type );
6438 ADD_DATA( parent_objp->net_signature );
6439 ADD_DATA( new_objp->net_signature );
6443 multi_io_send_to_all(data, packet_size);
6446 void send_asteroid_throw( object *objp )
6449 ubyte data[MAX_PACKET_SIZE], packet_type;
6451 BUILD_HEADER( ASTEROID_INFO );
6453 // this packet type is an asteroid throw
6454 packet_type = ASTEROID_THROW;
6455 ADD_DATA( packet_type );
6456 ADD_DATA( objp->net_signature );
6457 ADD_DATA( objp->pos );
6458 ADD_DATA( objp->phys_info.vel );
6460 multi_io_send_to_all(data, packet_size);
6463 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6466 ubyte data[MAX_PACKET_SIZE], packet_type;
6470 if ( hitpos != NULL )
6473 // build up an asteroid hit packet
6474 BUILD_HEADER( ASTEROID_INFO );
6475 packet_type = ASTEROID_HIT;
6476 ADD_DATA( packet_type );
6477 ADD_DATA( objp->net_signature );
6479 if(other_objp == NULL){
6480 ushort invalid_sig = 0xffff;
6481 ADD_DATA(invalid_sig);
6483 ADD_DATA( other_objp->net_signature );
6488 multi_io_send_to_all(data, packet_size);
6491 void process_asteroid_info( ubyte *data, header *hinfo )
6496 offset = HEADER_LENGTH;
6497 GET_DATA( packet_type );
6499 // based on the packet type, do something interesting with an asteroid!
6500 switch( packet_type ) {
6502 case ASTEROID_CREATE: {
6503 ushort psignature, signature;
6506 object *parent_objp;
6508 GET_DATA( psignature );
6509 GET_DATA( signature );
6513 // after getting the values, set the next network signature, and call the create sub function
6514 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6515 parent_objp = multi_get_network_object( psignature );
6516 if ( parent_objp ) {
6517 asteroid_sub_create( parent_objp, atype, &relvec );
6519 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6526 // asteroid throw packet -- asteroid has wrapped bounds
6527 case ASTEROID_THROW: {
6532 GET_DATA( signature );
6535 objp = multi_get_network_object( signature );
6537 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6541 objp->phys_info.vel = vel;
6542 objp->phys_info.desired_vel = vel;
6546 case ASTEROID_HIT: {
6547 ushort signature, osignature;
6548 object *objp, *other_objp;
6552 GET_DATA( signature );
6553 GET_DATA( osignature );
6557 objp = multi_get_network_object( signature );
6558 if(osignature == 0xffff){
6561 other_objp = multi_get_network_object( osignature );
6564 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6568 if ( IS_VEC_NULL(&hitpos) ){
6569 asteroid_hit( objp, other_objp, NULL, damage );
6571 asteroid_hit( objp, other_objp, &hitpos, damage );
6574 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6575 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6576 weapon_hit( other_objp, objp, &hitpos );
6589 void send_host_restr_packet(char *callsign,int code,int mode)
6591 ubyte data[MAX_PACKET_SIZE],val;
6592 int packet_size = 0;
6594 // build the header and add the opcode
6595 BUILD_HEADER(HOST_RESTR_QUERY);
6602 ADD_STRING(callsign);
6604 // if I'm the standalone server, I should be sending this to the game host
6605 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6606 multi_io_send_reliable(Netgame.host, data, packet_size);
6608 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6610 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6611 multi_io_send_reliable(Net_player, data, packet_size);
6615 void process_host_restr_packet(ubyte *data, header *hinfo)
6619 int offset = HEADER_LENGTH;
6621 // get the opcode and the callsign
6624 GET_STRING(callsign);
6627 // do code specific operations
6629 // query to the host from standalone
6631 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6633 // set the join mode
6634 Multi_join_restr_mode = mode;
6636 // set the timestamp
6637 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6639 // notify the host of the event
6640 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6641 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6644 // affirmative reply from the host to the standalone
6646 Assert(Game_mode & GM_STANDALONE_SERVER);
6648 // let the player join if the timestamp has not already elapsed on the server
6649 if(Multi_restr_query_timestamp != -1){
6650 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6656 Assert(Game_mode & GM_STANDALONE_SERVER);
6657 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6658 Multi_restr_query_timestamp = -1;
6663 void send_netgame_end_error_packet(int notify_code,int err_code)
6667 int packet_size = 0;
6669 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6670 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6672 // build the header and add the notification and error codes
6673 BUILD_HEADER(NETGAME_END_ERROR);
6674 code = (char)notify_code;
6676 code = (char)err_code;
6680 multi_io_send_to_all_reliable(data, packet_size);
6683 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6685 int offset = HEADER_LENGTH;
6686 char notify_code,error_code;
6688 // get the error and notification codes
6689 GET_DATA(notify_code);
6690 GET_DATA(error_code);
6694 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6697 // sends info that a countermeasure succeeded.
6698 void send_countermeasure_success_packet( int objnum )
6700 int pnum, packet_size;
6701 ubyte data[MAX_PACKET_SIZE];
6703 pnum = multi_find_player_by_object( &Objects[objnum] );
6705 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6709 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6710 multi_io_send(&Net_players[pnum], data, packet_size);
6713 // start the flashing of my hud gauge
6714 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6718 offset = HEADER_LENGTH;
6721 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6722 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6725 #define UPDATE_IS_PAUSED (1<<0)
6726 #define UPDATE_HULL_INFO (1<<1)
6728 void send_client_update_packet(net_player *pl)
6730 ubyte data[MAX_PACKET_SIZE],val;
6731 int packet_size = 0;
6734 BUILD_HEADER(CLIENT_UPDATE);
6738 // add the pause status
6739 if ( Multi_pause_status ) {
6740 val |= UPDATE_IS_PAUSED;
6741 } 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) ) {
6742 val |= UPDATE_HULL_INFO;
6743 Assert( Player_ship ); // I"d better have one of these!!!!
6748 // if paused, add the net address of the guy who paused
6749 if(val & UPDATE_IS_PAUSED){
6750 Assert(Multi_pause_pauser != NULL);
6751 ADD_DATA(Multi_pause_pauser->player_id);
6754 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6755 if ( val & UPDATE_HULL_INFO ) {
6757 ubyte percent, ns, threats;
6760 ship_subsys *subsysp;
6763 // get the object for the player
6764 Assert( pl->player->objnum != -1 );
6766 objp = &Objects[pl->player->objnum];
6768 Assert ( objp->type == OBJ_SHIP );
6769 shipp = &Ships[objp->instance];
6770 sip = &Ship_info[shipp->ship_info_index];
6772 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6773 // percentage value since that should be close enough
6774 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6778 percent = (ubyte)temp;
6780 ADD_DATA( percent );
6782 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6783 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6784 ADD_DATA( percent );
6787 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6788 // both machines. Added as percent since that number <= 100
6790 // also write out the number of subsystems. We do this because the client might not know
6791 // about the object he is getting data for. (i.e. he killed the object already).
6792 ns = (ubyte)sip->n_subsystems;
6795 // now the subsystems.
6796 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6797 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6798 ADD_DATA( percent );
6801 // compute the threats for this player. Only compute the threats if this player is actually
6802 // playing (i.e. he has a ship)
6803 hud_update_reticle( pl->player );
6804 threats = (ubyte)pl->player->threat_flags;
6805 ADD_DATA( threats );
6807 // add his energy level for guns
6808 ADD_DATA(shipp->weapon_energy);
6810 // add his secondary bank ammo
6811 ADD_DATA(shipp->weapons.num_secondary_banks);
6812 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6813 ADD_DATA(shipp->weapons.secondary_bank_ammo[i]);
6818 ADD_DATA(pl->sv_last_pl);
6820 // send the packet reliably to the player
6821 multi_io_send(pl, data, packet_size);
6824 void process_client_update_packet(ubyte *data, header *hinfo)
6829 int is_paused, have_hull_info;
6832 float weapon_energy;
6833 int offset = HEADER_LENGTH;
6835 // get the header byte containing useful information
6838 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6839 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6841 // if we are paused, get who paused
6844 player_index = find_player_id(pauser);
6845 if(player_index != -1){
6846 Multi_pause_pauser = &Net_players[player_index];
6848 Multi_pause_pauser = NULL;
6852 // if we have hull information, then read it in.
6853 if ( have_hull_info ) {
6857 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
6859 ship_subsys *subsysp;
6863 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6864 // percentage value since that should be close enough
6865 GET_DATA( hull_percent );
6867 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
6869 shield_percent[i] = ub_tmp;
6872 // get the data for the subsystems
6873 GET_DATA( n_subsystems );
6874 for ( i = 0; i < n_subsystems; i++ ){
6876 subsystem_percent[i] = ub_tmp;
6879 GET_DATA( threats );
6881 // add his energy level for guns
6882 GET_DATA(weapon_energy);
6884 // add his secondary bank ammo
6885 GET_DATA(ammo_count);
6886 for(i=0; i<ammo_count; i++){
6890 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
6891 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
6892 // UDP client update packets. Only read this info if I have a ship.
6893 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
6894 shipp = Player_ship;
6896 sip = &Ship_info[shipp->ship_info_index];
6898 val = hull_percent * sip->initial_hull_strength / 100.0f;
6899 objp->hull_strength = val;
6901 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6902 val = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
6903 objp->shields[i] = val;
6906 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
6907 // then don't read these in.
6908 if ( n_subsystems == sip->n_subsystems ) {
6910 n_subsystems = 0; // reuse this variable
6911 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6914 val = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
6915 subsysp->current_hits = val;
6917 // add the value just generated (it was zero'ed above) into the array of generic system types
6918 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
6919 Assert ( subsys_type < SUBSYSTEM_MAX );
6920 shipp->subsys_info[subsys_type].current_hits += val;
6924 ship_recalc_subsys_strength( shipp );
6926 shipp->weapon_energy = weapon_energy;
6927 for(i=0; i<ammo_count; i++){
6928 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
6931 // update my threat flags.
6932 // temporarily commented out until tested.
6933 Net_player->player->threat_flags = threats;
6940 if(Net_player != NULL){
6941 Net_player->cl_last_pl = pl;
6945 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
6946 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
6948 multi_pause_pause();
6950 multi_pause_unpause();
6955 void send_countdown_packet(int time)
6959 int packet_size = 0;
6961 // build the header and add the time
6962 BUILD_HEADER(COUNTDOWN);
6966 // if we're the server, we should broadcast to everyone
6967 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6968 multi_io_send_to_all_reliable(data, packet_size);
6970 // otherwise we'de better be a host sending to the standalone
6972 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6973 multi_io_send_reliable(Net_player, data, packet_size);
6977 void process_countdown_packet(ubyte *data, header *hinfo)
6979 int offset = HEADER_LENGTH;
6986 // if we're not in the post sync data screen, ignore it
6987 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
6991 // if we're the standalone, this should be a -1 telling us to start the countdown
6992 if(Game_mode & GM_STANDALONE_SERVER){
6993 Assert((int)time == -1);
6995 // start the countdown
6996 multi_sync_start_countdown();
6998 // otherwise if we're clients, just bash the countdown
7000 Multi_sync_countdown = (int)time;
7004 // packets for debriefing information
7005 void send_debrief_info( int stage_count[], int *stages[] )
7007 ubyte data[MAX_PACKET_SIZE];
7008 int packet_size, i, j;
7011 BUILD_HEADER(DEBRIEF_INFO);
7013 // add the data for the teams
7014 for ( i = 0; i < Num_teams; i++ ) {
7017 count = stage_count[i];
7019 for ( j = 0; j < count; j++ ) {
7020 i_tmp = stages[i][j];
7025 multi_io_send_to_all_reliable(data, packet_size);
7028 // process a debrief info packet from the server
7029 void process_debrief_info( ubyte *data, header *hinfo )
7032 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7035 offset = HEADER_LENGTH;
7036 for ( i = 0; i < Num_teams; i++ ) {
7040 stage_counts[i] = count;
7041 stages[i] = active_stages[i];
7042 for ( j = 0; j < count; j++ ) {
7044 active_stages[i][j] = i_tmp;
7049 // now that we have the stage data for the debriefing stages, call debrief function with the
7050 // data so that clients can now see the debriefing stuff. Do it only for my team.
7051 Assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7052 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7055 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7056 // sends homing_object and homing_subsystem to all clients.
7057 void send_homing_weapon_info( int weapon_num )
7059 ubyte data[MAX_PACKET_SIZE];
7062 object *homing_object;
7063 ushort homing_signature;
7066 wp = &Weapons[weapon_num];
7068 // be sure that this weapon object is a homing object.
7069 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7072 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7073 // homing signature.
7074 homing_signature = 0;
7075 homing_object = wp->homing_object;
7076 if ( homing_object != NULL ) {
7077 homing_signature = homing_object->net_signature;
7079 // get the subsystem index.
7081 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7084 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7085 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
7086 t_subsys = (char)s_index;
7090 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7091 ADD_DATA( Objects[wp->objnum].net_signature );
7092 ADD_DATA( homing_signature );
7093 ADD_DATA( t_subsys );
7095 multi_io_send_to_all(data, packet_size);
7098 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7099 // packet contains information for multiple weapons (like hornets).
7100 void process_homing_weapon_info( ubyte *data, header *hinfo )
7103 ushort weapon_signature, homing_signature;
7105 object *homing_object, *weapon_objp;
7108 offset = HEADER_LENGTH;
7110 // get the data for the packet
7111 GET_DATA( weapon_signature );
7112 GET_DATA( homing_signature );
7113 GET_DATA( h_subsys );
7116 // deal with changing this weapons homing information
7117 weapon_objp = multi_get_network_object( weapon_signature );
7118 if ( weapon_objp == NULL ) {
7119 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7122 Assert( weapon_objp->type == OBJ_WEAPON );
7123 wp = &Weapons[weapon_objp->instance];
7125 // be sure that we can find these weapons and
7126 homing_object = multi_get_network_object( homing_signature );
7127 if ( homing_object == NULL ) {
7128 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7132 if ( homing_object->type == OBJ_WEAPON ) {
7133 Assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7136 wp->homing_object = homing_object;
7137 wp->homing_subsys = NULL;
7138 wp->target_num = OBJ_INDEX(homing_object);
7139 wp->target_sig = homing_object->signature;
7140 if ( h_subsys != -1 ) {
7141 Assert( homing_object->type == OBJ_SHIP );
7142 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7145 if ( homing_object->type == OBJ_SHIP ) {
7146 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7150 void send_emp_effect(ushort net_sig, float intensity, float time)
7155 Assert(MULTIPLAYER_MASTER);
7157 // build the packet and add the opcode
7158 BUILD_HEADER(EMP_EFFECT);
7160 ADD_DATA(intensity);
7163 // send it to the player
7164 multi_io_send_to_all(data, packet_size);
7167 void process_emp_effect(ubyte *data, header *hinfo)
7169 float intensity, time;
7172 int offset = HEADER_LENGTH;
7174 // read in the EMP effect data
7176 GET_DATA(intensity);
7180 // try and find the object
7181 objp = multi_get_network_object(net_sig);
7182 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7183 // if i'm not an observer and I have a valid ship, play the EMP effect
7184 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7185 emp_start_local(intensity, time);
7188 // start the effect for the ship itself
7189 emp_start_ship(objp, intensity, time);
7193 // tells whether or not reinforcements are available
7194 void send_reinforcement_avail( int rnum )
7199 BUILD_HEADER(REINFORCEMENT_AVAIL);
7201 multi_io_send_to_all_reliable(data, packet_size);
7204 void process_reinforcement_avail( ubyte *data, header *hinfo )
7209 offset = HEADER_LENGTH;
7213 // sanity check for a valid reinforcement number
7214 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7215 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7219 void send_change_iff_packet(ushort net_signature, int new_team)
7221 ubyte data[MAX_PACKET_SIZE];
7222 int packet_size = 0;
7224 if(Net_player == NULL){
7227 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7231 // build the packet and add the data
7232 BUILD_HEADER(CHANGE_IFF);
7233 ADD_DATA(net_signature);
7236 // send to all players
7237 multi_io_send_to_all_reliable(data, packet_size);
7240 void process_change_iff_packet( ubyte *data, header *hinfo)
7242 int offset = HEADER_LENGTH;
7243 ushort net_signature;
7248 GET_DATA(net_signature);
7252 // lookup the object
7253 objp = multi_get_network_object(net_signature);
7254 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7255 Ships[objp->instance].team = new_team;
7259 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7261 int packet_size, objnum;
7262 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7265 net_player *ignore = NULL;
7267 // sanity checking for now
7268 Assert ( banks_fired <= 3 );
7270 // get an object pointer for this ship.
7271 objnum = shipp->objnum;
7272 objp = &Objects[objnum];
7274 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7275 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7279 // just in case nothing got fired
7280 if(banks_fired <= 0){
7284 // ubanks_fired = (ubyte)banks_fired;
7285 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7286 // Assert( current_bank <= 3 );
7288 // insert the current primary bank into this byte
7289 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7291 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7292 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7293 // ubanks_fired |= (1<<7);
7296 // determine if its a player ship and don't send to him if we're in "client firing" mode
7297 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7298 if(MULTIPLAYER_MASTER){
7299 np_index = multi_find_player_by_net_signature(objp->net_signature);
7300 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7301 ignore = &Net_players[np_index];
7305 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7306 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7307 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7308 // and server in sync w.r.t. weapon energy for player ship
7309 BUILD_HEADER( PRIMARY_FIRED_NEW );
7310 ADD_DATA(objp->net_signature);
7311 // ADD_DATA(ubanks_fired);
7313 // if I'm a server, broadcast to all players
7314 if(MULTIPLAYER_MASTER){
7315 multi_io_send_to_all(data, packet_size, ignore);
7318 multi_rate_add(1, "wfi", packet_size);
7320 // otherwise just send to the server
7322 multi_io_send(Net_player, data, packet_size);
7326 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7328 int offset; // linked;
7329 // ubyte banks_fired, current_bank;
7334 // read all packet info
7335 offset = HEADER_LENGTH;
7336 GET_DATA(shooter_sig);
7337 // GET_DATA(banks_fired);
7340 // find the object this fired packet is operating on
7341 objp = multi_get_network_object( shooter_sig );
7342 if ( objp == NULL ) {
7343 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7346 // if this object is not actually a valid ship, don't do anything
7347 if(objp->type != OBJ_SHIP){
7350 if(objp->instance < 0){
7353 shipp = &Ships[objp->instance];
7355 // get the link status of the primary banks
7357 // if ( banks_fired & (1<<7) ) {
7359 // banks_fired ^= (1<<7);
7362 // get the current primary bank
7363 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7364 // current_bank &= 0x3;
7365 // Assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7366 // shipp->weapons.current_primary_bank = current_bank;
7368 // strip off all remaining bits and just keep which banks were actually fired.
7369 // banks_fired &= 0x3;
7371 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7374 // shipp->flags &= ~SF_PRIMARY_LINKED;
7376 // shipp->flags |= SF_PRIMARY_LINKED;
7379 // if we're in client firing mode, ignore ones for myself
7380 if((Player_obj != NULL) && (Player_obj == objp)){
7384 ship_fire_primary( objp, 0, 1 );
7387 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7389 ubyte data[MAX_PACKET_SIZE];
7392 net_player *ignore = NULL;
7394 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7395 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7399 Assert ( cmeasure_count < UCHAR_MAX );
7400 BUILD_HEADER(COUNTERMEASURE_NEW);
7401 ADD_DATA( objp->net_signature );
7402 ADD_DATA( rand_val );
7404 nprintf(("Network","Sending NEW countermeasure packet!\n"));
7406 // determine if its a player ship and don't send to him if we're in "client firing" mode
7407 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7408 if(MULTIPLAYER_MASTER){
7409 np_index = multi_find_player_by_net_signature(objp->net_signature);
7410 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7411 ignore = &Net_players[np_index];
7415 // if I'm the server, send to all players
7416 if(MULTIPLAYER_MASTER){
7417 multi_io_send_to_all(data, packet_size, ignore);
7419 // otherwise send to the server
7421 multi_io_send(Net_player, data, packet_size);
7425 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7432 offset = HEADER_LENGTH;
7433 GET_DATA( signature );
7434 GET_DATA( rand_val );
7437 objp = multi_get_network_object( signature );
7438 if ( objp == NULL ) {
7439 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7442 if(objp->type != OBJ_SHIP){
7446 // if we're in client firing mode, ignore ones for myself
7447 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7448 if((Player_obj != NULL) && (Player_obj == objp)){
7452 // make it so ship can fire right away!
7453 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7454 if ( objp == Player_obj ){
7455 nprintf(("network", "firing countermeasure from my ship\n"));
7457 ship_launch_countermeasure( objp, rand_val );
7460 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7462 ubyte data[MAX_PACKET_SIZE];
7463 int packet_size = 0;
7467 // only the server should ever be doing this
7468 Assert(MULTIPLAYER_MASTER);
7470 // setup outgoing data
7471 Assert(shooter != NULL);
7472 Assert(turret != NULL);
7473 Assert(target != NULL);
7474 Assert(override != NULL);
7475 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7478 u_beam_info = (ubyte)beam_info_index;
7479 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7480 Assert(subsys_index >= 0);
7481 if(subsys_index < 0){
7486 BUILD_HEADER(BEAM_FIRED);
7487 ADD_DATA(shooter->net_signature);
7488 ADD_DATA(subsys_index);
7489 ADD_DATA(target->net_signature);
7490 ADD_DATA(u_beam_info);
7491 ADD_DATA((*override));
7493 // send to all clients
7494 multi_io_send_to_all_reliable(data, packet_size);
7497 void process_beam_fired_packet(ubyte *data, header *hinfo)
7500 ushort shooter_sig, target_sig;
7504 beam_fire_info fire_info;
7506 // only clients should ever get this
7507 Assert(MULTIPLAYER_CLIENT);
7509 // read in packet data
7510 offset = HEADER_LENGTH;
7511 GET_DATA(shooter_sig);
7512 GET_DATA(subsys_index);
7513 GET_DATA(target_sig);
7514 GET_DATA(u_beam_info);
7518 // lookup all relevant data
7519 fire_info.beam_info_index = (int)u_beam_info;
7520 fire_info.shooter = NULL;
7521 fire_info.target = NULL;
7522 fire_info.turret = NULL;
7523 fire_info.target_subsys = NULL;
7524 fire_info.beam_info_override = NULL;
7525 fire_info.shooter = multi_get_network_object(shooter_sig);
7526 fire_info.target = multi_get_network_object(target_sig);
7527 fire_info.beam_info_override = &b_info;
7528 fire_info.accuracy = 1.0f;
7529 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)){
7530 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7533 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7534 if(fire_info.turret == NULL){
7535 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7540 beam_fire(&fire_info);
7543 void send_sw_query_packet(ubyte code, char *txt)
7545 ubyte data[MAX_PACKET_SIZE];
7546 int packet_size = 0;
7548 // build the packet and add the code
7549 BUILD_HEADER(SW_STD_QUERY);
7551 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7555 // if I'm the host, send to standalone
7556 if(MULTIPLAYER_HOST){
7557 Assert(!MULTIPLAYER_MASTER);
7558 Assert(code == SW_STD_START);
7559 multi_io_send_reliable(Net_player, data, packet_size);
7561 // otherwise standalone sends back to host
7563 Assert(Game_mode & GM_STANDALONE_SERVER);
7564 Assert(code != SW_STD_START);
7565 Assert(Netgame.host != NULL);
7566 if(Netgame.host != NULL){
7567 multi_io_send_reliable(Netgame.host, data, packet_size);
7572 void process_sw_query_packet(ubyte *data, header *hinfo)
7576 void send_event_update_packet(int event)
7578 ubyte data[MAX_PACKET_SIZE];
7579 ushort u_event = (ushort)event;
7580 int packet_size = 0;
7582 // build the header and add the event
7583 BUILD_HEADER(EVENT_UPDATE);
7585 ADD_DATA(Mission_events[event].flags);
7586 ADD_DATA(Mission_events[event].formula);
7587 ADD_DATA(Mission_events[event].result);
7588 ADD_DATA(Mission_events[event].count);
7590 // send to all players
7591 multi_io_send_to_all_reliable(data, packet_size);
7594 void process_event_update_packet(ubyte *data, header *hinfo)
7596 int offset = HEADER_LENGTH;
7602 store_flags = Mission_events[u_event].flags;
7603 GET_DATA(Mission_events[u_event].flags);
7604 GET_DATA(Mission_events[u_event].formula);
7605 GET_DATA(Mission_events[u_event].result);
7606 GET_DATA(Mission_events[u_event].count);
7609 // went from non directive special to directive special
7610 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7611 mission_event_set_directive_special(u_event);
7613 // if we went directive special to non directive special
7614 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7615 mission_event_unset_directive_special(u_event);
7619 // weapon detonate packet
7620 void send_weapon_detonate_packet(object *objp)
7622 ubyte data[MAX_PACKET_SIZE];
7623 int packet_size = 0;
7626 Assert(MULTIPLAYER_MASTER);
7627 if(!MULTIPLAYER_MASTER){
7630 Assert(objp != NULL);
7635 // build the header and add the data
7636 BUILD_HEADER(WEAPON_DET);
7637 ADD_DATA(objp->net_signature);
7639 // send to all players
7640 multi_io_send_to_all(data, packet_size);
7643 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7646 int offset = HEADER_LENGTH;
7647 object *objp = NULL;
7649 // get the weapon signature
7653 // lookup the weapon
7654 objp = multi_get_network_object(net_sig);
7655 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7656 weapon_detonate(objp);
7660 // flak fired packet
7661 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7664 ushort pnet_signature;
7665 ubyte data[MAX_PACKET_SIZE], cindex;
7671 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7675 // local setup -- be sure we are actually passing a weapon!!!!
7676 objp = &Objects[weapon_objnum];
7677 Assert ( objp->type == OBJ_WEAPON );
7678 pnet_signature = Objects[ship_objnum].net_signature;
7680 Assert( subsys_index < UCHAR_MAX );
7681 cindex = (ubyte)subsys_index;
7683 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7688 // build the fire turret packet.
7689 BUILD_HEADER(FLAK_FIRED);
7690 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.fvec);
7691 ADD_DATA( pnet_signature );
7693 val = (short)ssp->submodel_info_1.angs.h;
7695 val = (short)ssp->submodel_info_2.angs.p;
7697 ADD_DATA( flak_range );
7699 multi_io_send_to_all(data, packet_size);
7701 multi_rate_add(1, "flk", packet_size);
7704 void process_flak_fired_packet(ubyte *data, header *hinfo)
7706 int offset, weapon_objnum, wid;
7707 ushort pnet_signature;
7715 short pitch, heading;
7718 // get the data for the turret fired packet
7719 offset = HEADER_LENGTH;
7720 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7721 GET_DATA( pnet_signature );
7722 GET_DATA( turret_index );
7723 GET_DATA( heading );
7725 GET_DATA( flak_range );
7726 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7729 objp = multi_get_network_object( pnet_signature );
7730 if ( objp == NULL ) {
7731 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7735 // if this isn't a ship, do nothing
7736 if ( objp->type != OBJ_SHIP ){
7740 // make an orientation matrix from the o_fvec
7741 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7743 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7744 // hack, but should be suitable.
7745 shipp = &Ships[objp->instance];
7746 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7750 wid = ssp->system_info->turret_weapon_type;
7751 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7755 // bash the position and orientation of the turret
7756 ssp->submodel_info_1.angs.h = (float)heading;
7757 ssp->submodel_info_2.angs.p = (float)pitch;
7759 // get the world position of the weapon
7760 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7762 // create the weapon object
7763 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7764 if (weapon_objnum != -1) {
7765 if ( Weapon_info[wid].launch_snd != -1 ) {
7766 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7769 // create a muzzle flash from a flak gun based upon firing position and weapon type
7770 flak_muzzle_flash(&pos, &dir, wid);
7772 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7773 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7777 #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);
7778 #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);
7780 // player pain packet
7781 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7783 ubyte data[MAX_PACKET_SIZE];
7786 int packet_size = 0;
7788 Assert(MULTIPLAYER_MASTER);
7789 if(!MULTIPLAYER_MASTER){
7797 // build the packet and add the code
7798 BUILD_HEADER(NETPLAYER_PAIN);
7799 windex = (ubyte)weapon_info_index;
7801 udamage = (ushort)damage;
7804 ADD_DATA((*hitpos));
7806 // send to the player
7807 multi_io_send(pl, data, packet_size);
7809 multi_rate_add(1, "pai", packet_size);
7812 void process_player_pain_packet(ubyte *data, header *hinfo)
7818 vector local_hit_pos;
7821 // get the data for the pain packet
7822 offset = HEADER_LENGTH;
7826 GET_DATA(local_hit_pos);
7829 mprintf(("PAIN!\n"));
7831 // get weapon info pointer
7832 Assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER));
7833 if(! ((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
7836 wip = &Weapon_info[windex];
7838 // play the weapon hit sound
7839 Assert(Player_obj != NULL);
7840 if(Player_obj == NULL){
7843 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
7845 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
7846 ship_hit_pain((float)udamage);
7849 ship_apply_whack(&force, &local_hit_pos, Player_obj);
7853 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
7855 ubyte data[MAX_PACKET_SIZE];
7857 int packet_size = 0;
7859 // build the header and add the data
7860 BUILD_HEADER(LIGHTNING_PACKET);
7861 val = (char)bolt_type;
7864 ADD_DATA((*strike));
7866 // send to everyone unreliable for now
7867 multi_io_send_to_all(data, packet_size);
7870 void process_lightning_packet(ubyte *data, header *hinfo)
7874 vector start, strike;
7877 offset = HEADER_LENGTH;
7878 GET_DATA(bolt_type);
7889 nebl_bolt(bolt_type, &start, &strike);
7892 void send_bytes_recvd_packet(net_player *pl)
7894 // only clients should ever be doing this
7899 ubyte data[MAX_PACKET_SIZE];
7900 int packet_size = 0;
7901 BUILD_HEADER(BYTES_SENT);
7902 ADD_DATA(pl->cl_bytes_recvd);
7904 // send to the server
7905 multi_io_send_reliable(pl, data, packet_size);
7908 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
7912 net_player *pl = NULL;
7913 int offset = HEADER_LENGTH;
7919 if(Net_player == NULL){
7922 if(!MULTIPLAYER_MASTER){
7926 // make sure we know what player sent this
7927 pid = find_player_id(hinfo->id);
7928 if((pid < 0) || (pid >= MAX_PLAYERS)){
7931 pl = &Net_players[pid];
7934 pl->cl_bytes_recvd = bytes;
7938 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
7941 pl->sv_bytes_sent = 0;
7945 void send_host_captain_change_packet(short player_id, int captain_change)
7947 ubyte data[MAX_PACKET_SIZE];
7948 int packet_size = 0;
7951 BUILD_HEADER(TRANSFER_HOST);
7952 ADD_DATA(player_id);
7953 ADD_DATA(captain_change);
7956 multi_io_send_to_all_reliable(data, packet_size);
7959 void process_host_captain_change_packet(ubyte *data, header *hinfo)
7961 int offset = HEADER_LENGTH;
7962 int idx, found_player, captain_change;
7965 // get the player id
7966 GET_DATA(player_id);
7967 GET_DATA(captain_change);
7973 for(idx=0; idx<MAX_PLAYERS; idx++){
7974 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
7975 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
7980 // unflag all old players
7981 for(idx=0; idx<MAX_PLAYERS; idx++){
7982 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
7987 for(idx=0; idx<MAX_PLAYERS; idx++){
7988 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
7989 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
7991 // spew to the HUD config
7992 if(Net_players[idx].player != NULL){
7993 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
8003 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
8008 void send_self_destruct_packet()
8010 ubyte data[MAX_PACKET_SIZE];
8011 int packet_size = 0;
8014 if(Net_player == NULL){
8018 // if i'm the server, I shouldn't be here
8019 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8020 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8024 // only if this is valid
8025 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8030 if((Player_ship == NULL) || (Player_obj == NULL)){
8035 BUILD_HEADER(SELF_DESTRUCT);
8036 ADD_DATA(Player_obj->net_signature);
8038 // send to the server
8039 multi_io_send_reliable(Net_player, data, packet_size);
8042 void process_self_destruct_packet(ubyte *data, header *hinfo)
8044 int offset = HEADER_LENGTH;
8048 // get the net signature
8053 np_index = find_player_id(hinfo->id);
8057 if(MULTI_OBSERVER(Net_players[np_index])){
8060 if(Net_players[np_index].player == NULL){
8063 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8066 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8069 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8072 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8077 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);