2 * $Logfile: /Freespace2/code/Network/MultiMsgs.cpp $
7 * C file that holds functions for the building and processing of multiplayer packets
10 * Revision 1.1 2002/05/03 03:28:10 root
14 * 83 9/14/99 2:21p Dave
15 * Fixed observer mode joining and ingame stuff.
17 * 82 9/14/99 3:26a Dave
18 * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
19 * respawn-too-early problem. Made a few crash points safe.
21 * 81 9/13/99 4:52p Dave
24 * 80 9/08/99 10:01p Dave
25 * Make sure game won't run in a drive's root directory. Make sure
26 * standalone routes suqad war messages properly to the host.
28 * 79 8/28/99 4:54p Dave
29 * Fixed directives display for multiplayer clients for wings with
30 * multiple waves. Fixed hud threat indicator rendering color.
32 * 78 8/27/99 12:32a Dave
33 * Allow the user to specify a local port through the launcher.
35 * 77 8/26/99 8:51p Dave
36 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
38 * 76 8/25/99 4:38p Dave
39 * Updated PXO stuff. Make squad war report stuff much more nicely.
41 * 75 8/24/99 1:50a Dave
42 * Fixed client-side afterburner stuttering. Added checkbox for no version
43 * checking on PXO join. Made button info passing more friendly between
46 * 74 8/22/99 5:53p Dave
47 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
48 * instead of ship designations for multiplayer players.
50 * 73 8/22/99 1:55p Dave
51 * Cleaned up host/team-captain leaving code.
53 * 72 8/22/99 1:19p Dave
54 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
55 * which d3d cards are detected.
57 * 71 8/19/99 10:59a Dave
58 * Packet loss detection.
60 * 70 8/17/99 1:12p Dave
61 * Send TvT update when a client has finished joining so he stays nice and
64 * 69 8/16/99 4:05p Dave
65 * Big honking checkin.
67 * 68 8/11/99 5:54p Dave
68 * Fixed collision problem. Fixed standalone ghost problem.
70 * 67 8/06/99 9:46p Dave
71 * Hopefully final changes for the demo.
73 * 66 8/05/99 2:06a Dave
76 * 65 7/30/99 7:01p Dave
77 * Dogfight escort gauge. Fixed up laser rendering in Glide.
79 * 64 7/29/99 5:41p Jefff
80 * Sound hooks for cmeasure success
82 * 63 7/28/99 5:34p Dave
83 * Nailed the missing stats bug to the wall. Problem was optimized build
84 * and using GET_DATA() with array elements. BLECH.
86 * 62 7/26/99 5:50p Dave
87 * Revised ingame join. Better? We'll see....
89 * 61 7/24/99 1:54p Dave
90 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
93 * 60 7/22/99 7:17p Dave
94 * Fixed excessive whacks in multiplayer.
96 * 59 7/08/99 10:53a Dave
97 * New multiplayer interpolation scheme. Not 100% done yet, but still
98 * better than the old way.
100 * 58 7/03/99 5:50p Dave
101 * Make rotated bitmaps draw properly in padlock views.
103 * 57 7/03/99 4:08p Dave
104 * Fixed wss_slots size issues. Fixed potentially nasty bug in low level
107 * 56 6/21/99 7:24p Dave
108 * netplayer pain packet. Added type E unmoving beams.
110 * 55 6/18/99 5:16p Dave
111 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
112 * dialog to PXO screen.
114 * 54 6/16/99 4:06p Dave
115 * New pilot info popup. Added new draw-bitmap-as-poly function.
117 * 53 6/16/99 10:20a Dave
118 * Added send-message-list sexpression.
120 * 52 6/04/99 3:52p Anoop
121 * Removed bogus assert.
123 * 51 6/01/99 8:35p Dave
124 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
125 * awacs-set-radius sexpression.
127 * 50 5/21/99 5:03p Andsager
128 * Add code to display engine wash death. Modify ship_kill_packet
130 * 49 5/18/99 1:30p Dave
131 * Added muzzle flash table stuff.
133 * 48 5/14/99 1:59p Andsager
134 * Multiplayer message for subsystem cargo revealed.
136 * 47 5/14/99 12:15p Andsager
137 * Add vaporized to kill packet
139 * 46 5/03/99 8:32p Dave
140 * New version of multi host options screen.
142 * 45 4/30/99 12:18p Dave
143 * Several minor bug fixes.
145 * 44 4/29/99 2:29p Dave
146 * Made flak work much better in multiplayer.
148 * 43 4/28/99 11:13p Dave
149 * Temporary checkin of artillery code.
151 * 42 4/16/99 5:54p Dave
152 * Support for on/off style "stream" weapons. Real early support for
153 * target-painting lasers.
155 * 41 4/12/99 2:22p Dave
156 * More checks for dogfight stats.
158 * 40 4/09/99 2:21p Dave
159 * Multiplayer beta stuff. CD checking.
161 * 39 4/02/99 9:55a Dave
162 * Added a few more options in the weapons.tbl for beam weapons. Attempt
163 * at putting "pain" packets into multiplayer.
165 * 38 4/01/99 3:41p Anoop
166 * Removed bogus Int3().
168 * 37 3/19/99 9:51a Dave
169 * Checkin to repair massive source safe crash. Also added support for
170 * pof-style nebulae, and some new weapons code.
172 * 38 3/12/99 2:32p Anoop
173 * Removed bogus asserts.
175 * 37 3/11/99 11:41a Neilk
176 * Don't do multi_io_* operations in single-player
178 * 36 3/10/99 6:50p Dave
179 * Changed the way we buffer packets for all clients. Optimized turret
180 * fired packets. Did some weapon firing optimizations.
182 * 35 3/09/99 6:24p Dave
183 * More work on object update revamping. Identified several sources of
184 * unnecessary bandwidth.
186 * 34 3/08/99 7:03p Dave
187 * First run of new object update system. Looks very promising.
189 * 33 3/04/99 6:09p Dave
190 * Added in sexpressions for firing beams and checking for if a ship is
193 * 32 3/01/99 10:00a Dave
194 * Fxied several dogfight related stats bugs.
196 * 31 2/24/99 2:25p Dave
197 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
198 * bug for dogfight more.
200 * 30 2/23/99 2:29p Dave
201 * First run of oldschool dogfight mode.
203 * 29 2/21/99 6:01p Dave
204 * Fixed standalone WSS packets.
206 * 28 2/21/99 1:48p Dave
207 * Some code for monitoring datarate for multiplayer in detail.
209 * 27 2/17/99 2:11p Dave
210 * First full run of squad war. All freespace and tracker side stuff
213 * 26 2/12/99 6:16p Dave
214 * Pre-mission Squad War code is 95% done.
216 * 25 2/11/99 3:08p Dave
217 * PXO refresh button. Very preliminary squad war support.
219 * 24 1/29/99 5:07p Dave
220 * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
223 * 23 1/27/99 9:56a Dave
224 * Temporary checkin of beam weapons for Dan to make cool sounds.
226 * 22 1/26/99 6:33p Anoop
227 * Fixed multiplayer slot switching problem (be sure to remember that
228 * hinfo->id is player id# _not_ player index #)
230 * 21 1/24/99 11:37p Dave
231 * First full rev of beam weapons. Very customizable. Removed some bogus
232 * Int3()'s in low level net code.
234 * 20 1/15/99 4:37p Dave
235 * Potential fix for weapon pair problem.
237 * 19 1/14/99 6:06p Dave
238 * 100% full squad logo support for single player and multiplayer.
240 * 18 1/14/99 12:48a Dave
241 * Todo list bug fixes. Made a pass at putting briefing icons back into
242 * FRED. Sort of works :(
244 * 17 1/12/99 5:45p Dave
245 * Moved weapon pipeline in multiplayer to almost exclusively client side.
246 * Very good results. Bandwidth goes down, playability goes up for crappy
247 * connections. Fixed object update problem for ship subsystems.
249 * 16 1/08/99 4:56p Anoop
250 * Fixed a problem with wss request packets.
252 * 15 12/18/98 12:24p Markm
253 * Fixed a dumb bug where player image_filenames were not being passed
254 * properly in new players packet.
256 * 14 12/14/98 12:13p Dave
257 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
260 * 13 11/30/98 1:07p Dave
261 * 16 bit conversion, first run.
263 * 12 11/20/98 4:08p Dave
264 * Fixed flak effect in multiplayer.
266 * 11 11/19/98 4:19p Dave
267 * Put IPX sockets back in psnet. Consolidated all multiplayer config
270 * 10 11/19/98 8:04a Dave
271 * Full support for D3-style reliable sockets. Revamped packet lag/loss
272 * system, made it receiver side and at the lowest possible level.
274 * 9 11/17/98 11:12a Dave
275 * Removed player identification by address. Now assign explicit id #'s.
277 * 8 11/12/98 11:50a Dave
278 * Multiplayer clients set flak range to be very long.
280 * 7 11/12/98 12:13a Dave
281 * Tidied code up for multiplayer test. Put in network support for flak
284 * 6 11/05/98 5:55p Dave
285 * Big pass at reducing #includes
287 * 5 10/20/98 1:39p Andsager
288 * Make so sparks follow animated ship submodels. Modify
289 * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
290 * submodel_num. Add submodel_num to multiplayer hit packet.
292 * 4 10/13/98 9:29a Dave
293 * Started neatening up freespace.h. Many variables renamed and
294 * reorganized. Added AlphaColors.[h,cpp]
296 * 3 10/07/98 6:27p Dave
297 * Globalized mission and campaign file extensions. Removed Silent Threat
298 * special code. Moved \cache \players and \multidata into the \data
301 * 2 10/07/98 10:53a Dave
304 * 1 10/07/98 10:50a Dave
306 * 506 10/02/98 3:22p Allender
307 * fix up the -connect option and fix the -port option
309 * 505 10/02/98 11:45a Dave
310 * Fixed stupid chat message bug.
312 * 504 9/29/98 1:33p Dave
313 * Remove standalone only conditional compiles for pre 1.04 stuff.
315 * 503 9/28/98 1:54p Dave
316 * Make sure French and German don't xfer builtin files they don't have
319 * 502 9/20/98 7:19p Dave
320 * Added CHANGE_IFF packet.
322 * 501 9/17/98 3:08p Dave
323 * PXO to non-pxo game warning popup. Player icon stuff in create and join
324 * game screens. Upped server count refresh time in PXO to 35 secs (from
332 #include <io.h> // for findfirst/findnext, etc
334 #include "multimsgs.h"
335 #include "multiutil.h"
338 #include "multiteamselect.h"
339 #include "linklist.h"
340 #include "gamesequence.h"
341 #include "hudmessage.h"
342 #include "hudsquadmsg.h"
343 #include "freespace.h"
347 #include "missiongoals.h"
348 #include "missionparse.h"
349 #include "missionlog.h"
350 #include "missionmessage.h"
351 #include "missionbrief.h"
353 #include "cmeasure.h"
354 #include "model.h" // for some limits
355 #include "afterburner.h"
356 #include "stand_gui.h"
357 #include "multi_xfer.h"
363 #include "managepilot.h"
364 #include "hudsquadmsg.h"
366 #include "missionweaponchoice.h"
367 #include "missionshipchoice.h"
368 #include "fireballs.h"
371 #include "multi_ingame.h"
372 #include "multiteamselect.h"
374 #include "multi_campaign.h"
375 #include "multi_team.h"
376 #include "multi_respawn.h"
377 #include "multi_observer.h"
378 #include "multi_voice.h"
379 #include "asteroid.h"
380 #include "multi_pmsg.h"
381 #include "multi_data.h"
382 #include "multi_options.h"
383 #include "objcollide.h"
384 #include "hudreticle.h"
385 #include "multi_pause.h"
386 #include "multi_endgame.h"
387 #include "missiondebrief.h"
388 #include "multi_obj.h"
389 #include "multi_log.h"
391 #include "multi_kick.h"
395 #include "multi_rate.h"
396 #include "neblightning.h"
397 #include "hudescort.h"
399 // #define _MULTI_SUPER_WACKY_COMPRESSION
401 #ifdef _MULTI_SUPER_WACKY_COMPRESSION
403 #define MAX_CODE ( ( 1 << BITS ) - 1 )
404 #define TABLE_SIZE 35023L
405 #define END_OF_STREAM 256
406 #define BUMP_CODE 257
407 #define FLUSH_CODE 258
408 #define FIRST_CODE 259
417 static DICTIONARY dict[TABLE_SIZE];
418 static char decode_stack[TABLE_SIZE];
419 static uint next_code;
420 static int current_code_bits;
421 static uint next_bump_code;
423 typedef struct BitBuf {
429 void output_bits( BitBuf *bitbuf, uint code, int count )
433 mask = 1L << ( count - 1 );
436 bitbuf->rack |= bitbuf->mask;
438 if ( bitbuf->mask == 0 ) {
439 *bitbuf->data++=(ubyte)bitbuf->rack;
447 uint input_bits( BitBuf *bitbuf, int bit_count )
452 mask = 1L << ( bit_count - 1 );
455 if ( bitbuf->mask == 0x80 ) {
456 bitbuf->rack = *bitbuf->data++;
457 if ( bitbuf->rack == EOF )
458 return END_OF_STREAM;
460 if ( bitbuf->rack & bitbuf->mask )
461 return_value |= mask;
464 if ( bitbuf->mask == 0 )
467 return( return_value );
471 static void InitializeDictionary()
475 for ( i = 0 ; i < TABLE_SIZE ; i++ )
476 dict[i].code_value = UNUSED;
478 next_code = FIRST_CODE;
479 current_code_bits = 9;
480 next_bump_code = 511;
484 static uint find_child_node( int parent_code, int child_character )
489 index = ( child_character << ( BITS - 8 ) ) ^ parent_code;
493 offset = TABLE_SIZE - index;
495 if ( dict[ index ].code_value == UNUSED )
496 return( (uint) index );
497 if ( dict[ index ].parent_code == parent_code &&
498 dict[ index ].character == (char) child_character )
500 if ( (int) index >= offset )
503 index += TABLE_SIZE - offset;
508 static uint decode_string( uint count, uint code )
510 while ( code > 255 ) {
511 decode_stack[ count++ ] = dict[ code ].character;
512 code = dict[ code ].parent_code;
514 decode_stack[ count++ ] = (char) code;
518 int lzw_compress( ubyte *outputbuf, ubyte *inputbuf, int input_size )
526 // Init output bit buffer
529 output.data = outputbuf;
531 InitializeDictionary();
533 string_code = *inputbuf++;
535 for ( i=1 ; i<input_size ; i++ ) {
536 character = *inputbuf++;
537 index = find_child_node( string_code, character );
538 if ( dict[ index ].code_value != - 1 )
539 string_code = dict[ index ].code_value;
541 dict[ index ].code_value = next_code++;
542 dict[ index ].parent_code = string_code;
543 dict[ index ].character = (char) character;
544 output_bits( &output, (unsigned long) string_code, current_code_bits );
545 string_code = character;
546 if ( next_code > MAX_CODE ) {
547 output_bits( &output, (unsigned long) FLUSH_CODE, current_code_bits );
548 InitializeDictionary();
549 } else if ( next_code > next_bump_code ) {
550 output_bits( &output, (unsigned long) BUMP_CODE, current_code_bits );
552 next_bump_code <<= 1;
557 output_bits( &output, (unsigned long) string_code, current_code_bits );
558 output_bits( &output, (unsigned long) END_OF_STREAM, current_code_bits);
560 if ( output.mask != 0x80 )
561 *output.data++ = (ubyte)output.rack;
563 return output.data-outputbuf;
567 int lzw_expand( ubyte *outputbuf, ubyte *inputbuf )
578 input.data = inputbuf;
582 InitializeDictionary();
583 old_code = (uint) input_bits( &input, current_code_bits );
584 if ( old_code == END_OF_STREAM )
586 character = old_code;
587 outputbuf[counter++] = ( ubyte )old_code;
589 new_code = (uint) input_bits( &input, current_code_bits );
590 if ( new_code == END_OF_STREAM )
592 if ( new_code == FLUSH_CODE )
594 if ( new_code == BUMP_CODE ) {
598 if ( new_code >= next_code ) {
599 decode_stack[ 0 ] = (char) character;
600 count = decode_string( 1, old_code );
602 count = decode_string( 0, new_code );
604 character = decode_stack[ count - 1 ];
606 outputbuf[counter++] = ( ubyte )decode_stack[ --count ];
607 dict[ next_code ].parent_code = old_code;
608 dict[ next_code ].character = (char) character;
616 // send the specified data packet to all players
617 void multi_io_send(net_player *pl, ubyte *data, int len)
620 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
624 // don't do it for single player
625 if(!(Game_mode & GM_MULTIPLAYER)){
630 if(MULTIPLAYER_CLIENT){
631 // Assert(pl == Net_player);
632 if(pl != Net_player){
636 // Assert(pl != Net_player);
637 if(pl == Net_player){
642 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
643 if ((pl->s_info.unreliable_buffer_size + len) > MAX_PACKET_SIZE) {
644 multi_io_send_force(pl);
645 pl->s_info.unreliable_buffer_size = 0;
648 Assert((pl->s_info.unreliable_buffer_size + len) <= MAX_PACKET_SIZE);
650 memcpy(pl->s_info.unreliable_buffer + pl->s_info.unreliable_buffer_size, data, len);
651 pl->s_info.unreliable_buffer_size += len;
654 void multi_io_send_to_all(ubyte *data, int length, net_player *ignore)
657 Assert(MULTIPLAYER_MASTER);
659 // need to check for i > 1, hmmm... and connected. I don't know.
660 for (i = 0; i < MAX_PLAYERS; i++ ) {
661 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
665 // maybe ignore a player
666 if((ignore != NULL) && (&Net_players[i] == ignore)){
670 // ingame joiners not waiting to select a ship doesn't get any packets
671 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
676 multi_io_send(&Net_players[i], data, length);
680 void multi_io_send_force(net_player *pl)
683 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
687 // don't do it for single player
688 if(!(Game_mode & GM_MULTIPLAYER)){
692 // send everything in
693 if (MULTIPLAYER_MASTER) {
694 psnet_send(&pl->p_info.addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
696 // add the bytes sent to this player
697 pl->sv_bytes_sent += pl->s_info.unreliable_buffer_size;
699 psnet_send(&Netgame.server_addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
701 pl->s_info.unreliable_buffer_size = 0;
704 // send the data packet to all players via their reliable sockets
705 void multi_io_send_reliable(net_player *pl, ubyte *data, int len)
708 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
712 // don't do it for single player
713 if(!(Game_mode & GM_MULTIPLAYER)){
718 if(MULTIPLAYER_CLIENT){
719 // Assert(pl == Net_player);
720 if(pl != Net_player){
724 // Assert(pl != Net_player);
725 if(pl == Net_player){
730 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
731 if ((pl->s_info.reliable_buffer_size + len) > MAX_PACKET_SIZE) {
732 multi_io_send_reliable_force(pl);
733 pl->s_info.reliable_buffer_size = 0;
736 Assert((pl->s_info.reliable_buffer_size + len) <= MAX_PACKET_SIZE);
738 memcpy(pl->s_info.reliable_buffer + pl->s_info.reliable_buffer_size, data, len);
739 pl->s_info.reliable_buffer_size += len;
742 void multi_io_send_to_all_reliable(ubyte* data, int length, net_player *ignore)
745 Assert(MULTIPLAYER_MASTER);
747 // need to check for i > 1, hmmm... and connected. I don't know.
748 for (i = 0; i < MAX_PLAYERS; i++ ) {
749 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
753 // maybe ignore a player
754 if((ignore != NULL) && (&Net_players[i] == ignore)){
758 // ingame joiners not waiting to select a ship doesn't get any packets
759 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
764 multi_io_send_reliable(&Net_players[i], data, length);
768 void multi_io_send_reliable_force(net_player *pl)
771 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
775 // don't do it for single player
776 if(!(Game_mode & GM_MULTIPLAYER)){
780 // send everything in
781 if(MULTIPLAYER_MASTER) {
782 psnet_rel_send(pl->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
783 } else if(Net_player != NULL){
784 psnet_rel_send(Net_player->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
786 pl->s_info.reliable_buffer_size = 0;
789 // send all buffered packets
790 void multi_io_send_buffered_packets()
794 // don't do it for single player
795 if(!(Game_mode & GM_MULTIPLAYER)){
800 if(MULTIPLAYER_MASTER){
801 for(idx=0; idx<MAX_PLAYERS; idx++){
802 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
803 // force unreliable data
804 if(Net_players[idx].s_info.unreliable_buffer_size > 0){
805 multi_io_send_force(&Net_players[idx]);
806 Net_players[idx].s_info.unreliable_buffer_size = 0;
809 // force reliable data
810 if(Net_players[idx].s_info.reliable_buffer_size > 0){
811 multi_io_send_reliable_force(&Net_players[idx]);
812 Net_players[idx].s_info.reliable_buffer_size = 0;
818 else if(Net_player != NULL){
819 // force unreliable data
820 if(Net_player->s_info.unreliable_buffer_size > 0){
821 multi_io_send_force(Net_player);
822 Net_player->s_info.unreliable_buffer_size = 0;
825 // force reliable data
826 if(Net_player->s_info.reliable_buffer_size > 0){
827 multi_io_send_reliable_force(Net_player);
828 Net_player->s_info.reliable_buffer_size = 0;
833 // 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)
834 void send_game_chat_packet(net_player *from, char *msg, int msg_mode, net_player *to, char *expr, int server_msg)
836 ubyte data[MAX_PACKET_SIZE],mode;
839 BUILD_HEADER(GAME_CHAT);
842 ADD_DATA(from->player_id);
844 // add the message mode and if in MSG_TARGET mode, add who the target is
845 ADD_DATA(server_msg);
846 mode = (ubyte)msg_mode;
849 case MULTI_MSG_TARGET:
851 ADD_DATA(to->player_id);
854 Assert(expr != NULL);
858 // add the message itself
861 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
863 // message all players
865 for(idx=0;idx<MAX_PLAYERS;idx++){
866 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from)){
867 multi_io_send_reliable(&Net_players[idx], data, packet_size);
872 // message only friendly players
873 case MULTI_MSG_FRIENDLY:
874 for(idx=0;idx<MAX_PLAYERS;idx++){
875 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)){
876 multi_io_send_reliable(&Net_players[idx], data, packet_size);
881 // message only hostile players
882 case MULTI_MSG_HOSTILE:
883 for(idx=0;idx<MAX_PLAYERS;idx++){
884 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)){
885 multi_io_send_reliable(&Net_players[idx], data, packet_size);
890 // message the player's target
891 case MULTI_MSG_TARGET:
893 if(MULTI_CONNECTED((*to)) && !MULTI_STANDALONE((*to))){
894 multi_io_send_reliable(to, data, packet_size);
898 // message all players who match the expression string
900 Assert(expr != NULL);
901 for(idx=0;idx<MAX_PLAYERS;idx++){
902 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && multi_msg_matches_expr(&Net_players[idx],expr) ){
903 multi_io_send_reliable(&Net_players[idx], data, packet_size);
909 // send to the server, who will take care of routing it
911 multi_io_send_reliable(Net_player, data, packet_size);
915 // process a general game chat packet, if we're the standalone we should rebroadcast
916 void process_game_chat_packet( ubyte *data, header *hinfo )
920 int color_index,player_index,to_player_index,should_display,server_msg;
921 char msg[MULTI_MSG_MAX_TEXT_LEN+CALLSIGN_LEN+2];
925 offset = HEADER_LENGTH;
927 // get the id of the sender
930 // determine if this is a server message
931 GET_DATA(server_msg);
936 // if targeting a specific player, get the address
939 case MULTI_MSG_TARGET:
946 // get the message itself
950 // get the index of the sending player
951 color_index = find_player_id(from);
952 player_index = color_index;
954 // if we couldn't find the player - bail
955 if(player_index == -1){
956 nprintf(("Network","Could not find player for processing game chat packet!\n"));
962 // if we're the server, determine what to do with the packet here
963 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
964 // if he's targeting a specific player, find out who it is
965 if(mode == MULTI_MSG_TARGET){
966 to_player_index = find_player_id(to);
968 to_player_index = -1;
971 // if we couldn't find who sent the message or who should be getting the message, the bail
972 if(((to_player_index == -1) && (mode == MULTI_MSG_TARGET)) || (player_index == -1)){
976 // determine if _I_ should be seeing the text
977 if(Game_mode & GM_STANDALONE_SERVER){
980 // check against myself for several specific cases
982 if((mode == MULTI_MSG_ALL) ||
983 ((mode == MULTI_MSG_FRIENDLY) && (Net_player->p_info.team == Net_players[player_index].p_info.team)) ||
984 ((mode == MULTI_MSG_HOSTILE) && (Net_player->p_info.team != Net_players[player_index].p_info.team)) ||
985 ((mode == MULTI_MSG_TARGET) && (MY_NET_PLAYER_NUM == to_player_index)) ||
986 ((mode == MULTI_MSG_EXPR) && multi_msg_matches_expr(Net_player,expr)) ){
991 // if we're the server of a game, we need to rebroadcast to all other players
993 // individual target mission
994 case MULTI_MSG_TARGET:
995 // if I was the inteneded target, or we couldn't find the intended target, don't rebroadcast
996 if(to_player_index != MY_NET_PLAYER_NUM){
997 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, &Net_players[to_player_index], NULL, server_msg);
1001 case MULTI_MSG_EXPR:
1002 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, expr, server_msg);
1006 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, NULL, server_msg);
1010 // if a client receives this packet, its always ok for him to display it
1015 // if we're not on a standalone
1017 if(server_msg == 2){
1020 multi_display_chat_msg(msg, player_index, !server_msg);
1025 // broadcast a hud message to all players
1026 void send_hud_msg_to_all( char* msg )
1028 ubyte data[MAX_PACKET_SIZE];
1031 // only the server should be sending this packet
1032 BUILD_HEADER(HUD_MSG);
1036 multi_io_send_to_all( data, packet_size );
1039 // process an incoming hud message packet
1040 void process_hud_message(ubyte* data, header* hinfo)
1043 char msg_buffer[255];
1045 offset = HEADER_LENGTH;
1047 GET_STRING(msg_buffer);
1050 // this is the only safe place to do this since only in the mission is the HUD guaranteed to be inited
1051 if(Game_mode & GM_IN_MISSION){
1052 HUD_printf(msg_buffer);
1056 // send a join packet request to the specified address (should be a server)
1057 void send_join_packet(net_addr* addr,join_request *jr)
1059 ubyte data[MAX_PACKET_SIZE];
1062 // build the header and add the request
1066 psnet_send(addr, data, packet_size);
1069 // process an incoming join request packet
1070 void process_join_packet(ubyte* data, header* hinfo)
1075 int host_restr_mode;
1076 // int team0_avail,team1_avail;
1077 char join_string[255];
1080 // only the server of the game should ever receive this packet
1081 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
1084 offset = HEADER_LENGTH;
1086 // read in the request info
1087 memset(&jr,0,sizeof(join_request));
1093 // fill in the address information of where this came from
1094 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
1096 // determine if we should accept this guy, or return a reason we should reject him
1097 // see the DENY_* codes in multi.h
1098 ret_code = multi_eval_join_request(&jr,&addr);
1100 // evaluate the return code
1102 // he should be accepted
1106 // we have to query the host because this is a restricted game
1107 case JOIN_QUERY_RESTRICTED :
1108 if(!(Game_mode & GM_STANDALONE_SERVER)){
1109 // notify the host of the event
1110 snd_play(&Snds[SND_CUE_VOICE]);
1113 // set the query timestamp
1114 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
1115 Netgame.flags |= NG_FLAG_INGAME_JOINING;
1117 // determine what mode we're in
1118 host_restr_mode = -1;
1119 memset(join_string,0,255);
1120 // if(Netgame.type == NG_TYPE_TEAM){
1121 // multi_player_ships_available(&team0_avail,&team1_avail);
1123 // if(team0_avail && team1_avail){
1124 // host_restr_mode = MULTI_JOIN_RESTR_MODE_4;
1125 // sprintf(join_string,"Player %s has tried to join. Accept on team 1 or 2 ?",jr.callsign);
1126 // } else if(team0_avail && !team1_avail){
1127 // host_restr_mode = MULTI_JOIN_RESTR_MODE_2;
1128 // sprintf(join_string,"Player %s has tried to join team 0, accept y/n ? ?",jr.callsign);
1129 // } else if(!team0_avail && team1_avail){
1130 // host_restr_mode = MULTI_JOIN_RESTR_MODE_3;
1131 // sprintf(join_string,"Player %s has tried to join team 1, accept y/n ?",jr.callsign);
1133 // } else if(Netgame.mode == NG_MODE_RESTRICTED){
1134 host_restr_mode = MULTI_JOIN_RESTR_MODE_1;
1135 sprintf(join_string,XSTR("Player %s has tried to join, accept y/n ?",715),jr.callsign);
1137 Assert(host_restr_mode != -1);
1139 // store the request info
1140 memcpy(&Multi_restr_join_request,&jr,sizeof(join_request));
1141 memcpy(&Multi_restr_addr,&addr,sizeof(net_addr));
1142 Multi_join_restr_mode = host_restr_mode;
1144 // if i'm the standalone server, I need to send a query to the host
1145 if(Game_mode & GM_STANDALONE_SERVER){
1146 send_host_restr_packet(jr.callsign,0,Multi_join_restr_mode);
1148 HUD_printf(join_string);
1152 ml_printf(NOX("Receive restricted join request from %s"), jr.callsign);
1156 // he'e being denied for some reason
1158 // send him the reason he is being denied
1159 send_deny_packet(&addr,ret_code);
1163 // process the rest of the request
1164 multi_process_valid_join_request(&jr,&addr);
1167 // send a notification that a new player has joined the game (if target != NULL, broadcast the packet)
1168 void send_new_player_packet(int new_player_num,net_player *target)
1170 ubyte data[MAX_PACKET_SIZE], val;
1171 int packet_size = 0;
1173 BUILD_HEADER( NOTIFY_NEW_PLAYER );
1175 // add the new player's info
1176 ADD_DATA(new_player_num);
1177 ADD_DATA(Net_players[new_player_num].p_info.addr);
1178 ADD_DATA(Net_players[new_player_num].player_id);
1179 ADD_DATA(Net_players[new_player_num].flags);
1180 ADD_STRING(Net_players[new_player_num].player->callsign);
1181 ADD_STRING(Net_players[new_player_num].player->image_filename);
1182 ADD_STRING(Net_players[new_player_num].player->squad_filename);
1183 ADD_STRING(Net_players[new_player_num].p_info.pxo_squad_name);
1185 val = (ubyte)Net_players[new_player_num].p_info.team;
1188 // broadcast the data
1190 multi_io_send_reliable(target, data, packet_size);
1192 multi_io_send_to_all_reliable(data, packet_size);
1196 // process a notification for a new player who has joined the game
1197 void process_new_player_packet(ubyte* data, header* hinfo)
1199 int already_in_game = 0;
1200 int offset, new_player_num,player_num,new_flags;
1202 char new_player_name[CALLSIGN_LEN+2] = "";
1203 char new_player_image[MAX_FILENAME_LEN+1] = "";
1204 char new_player_squad[MAX_FILENAME_LEN+1] = "";
1205 char new_player_pxo_squad[LOGIN_LEN+1] = "";
1206 char notify_string[256];
1210 offset = HEADER_LENGTH;
1212 // get the new players information
1213 GET_DATA(new_player_num);
1216 GET_DATA(new_flags);
1217 GET_STRING(new_player_name);
1218 GET_STRING(new_player_image);
1219 GET_STRING(new_player_squad);
1220 GET_STRING(new_player_pxo_squad);
1224 player_num = multi_find_open_player_slot();
1225 Assert(player_num != -1);
1227 // note that this new code does not check for duplicate IPs. It merely checks to see if
1228 // the slot referenced by new_player_num is already occupied by a connected player
1229 if(MULTI_CONNECTED(Net_players[new_player_num])){
1233 // if he's not alreayd in the game for one reason or another
1234 if ( !already_in_game ) {
1235 if ( Game_mode & GM_IN_MISSION ){
1236 HUD_sourced_printf(HUD_SOURCE_COMPUTER, XSTR("%s has entered the game\n",716), new_player_name);
1239 // create the player
1240 memcpy(new_addr.net_id, Psnet_my_addr.net_id, 4);
1242 if(new_flags & NETINFO_FLAG_OBSERVER){
1243 multi_obs_create_player(new_player_num,new_player_name,&new_addr,&Players[player_num]);
1244 Net_players[new_player_num].flags |= new_flags;
1246 multi_create_player( new_player_num, &Players[player_num],new_player_name, &new_addr, -1, new_id );
1247 Net_players[new_player_num].flags |= new_flags;
1250 // copy in the filename
1251 if(strlen(new_player_image) > 0){
1252 strcpy(Net_players[new_player_num].player->image_filename, new_player_image);
1254 strcpy(Net_players[new_player_num].player->image_filename, "");
1256 // copy his pilot squad filename
1257 Net_players[new_player_num].player->insignia_texture = -1;
1258 player_set_squad_bitmap(Net_players[new_player_num].player, new_player_squad);
1260 // copy in his pxo squad name
1261 strcpy(Net_players[new_player_num].p_info.pxo_squad_name, new_player_pxo_squad);
1263 // since we just created the player, set the last_heard_time here.
1264 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1266 Net_players[new_player_num].p_info.team = team;
1268 Net_players[new_player_num].player_id = new_id;
1270 // zero out this players ping
1271 multi_ping_reset(&Net_players[new_player_num].s_info.ping);
1273 // add a chat message
1274 if(Net_players[new_player_num].player->callsign != NULL){
1275 sprintf(notify_string,XSTR("<%s has joined>",717),Net_players[new_player_num].player->callsign);
1276 multi_display_chat_msg(notify_string,0,0);
1281 ml_printf(NOX("Received notification of new player %s"), Net_players[new_player_num].player->callsign);
1283 // let the current ui screen know someone joined
1284 switch(gameseq_get_state()){
1285 case GS_STATE_MULTI_HOST_SETUP :
1286 multi_create_handle_join(&Net_players[new_player_num]);
1288 case GS_STATE_MULTI_CLIENT_SETUP :
1289 multi_jw_handle_join(&Net_players[new_player_num]);
1294 #define PLAYER_DATA_SLOP 100
1296 void send_accept_player_data( net_player *npp, int is_ingame )
1300 ubyte data[MAX_PACKET_SIZE], stop;
1302 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1304 // add in the netplayer data for all players
1306 for (i=0; i<MAX_PLAYERS; i++) {
1307 // skip non connected players
1308 if ( !MULTI_CONNECTED(Net_players[i]) ){
1312 // skip this new player's entry
1313 if ( npp->player_id == Net_players[i].player_id ){
1317 // add the stop byte
1320 // add the player's number
1323 // add the player's address
1324 ADD_DATA(Net_players[i].p_info.addr);
1327 ADD_DATA(Net_players[i].player_id);
1330 ADD_STRING(Net_players[i].player->callsign);
1332 // add his image filename
1333 ADD_STRING(Net_players[i].player->image_filename);
1335 // add his squad filename
1336 ADD_STRING(Net_players[i].player->squad_filename);
1338 // add his PXO squad name
1339 ADD_STRING(Net_players[i].p_info.pxo_squad_name);
1342 ADD_DATA(Net_players[i].flags);
1344 // add his object's net sig
1346 ADD_DATA( Objects[Net_players[i].player->objnum].net_signature );
1349 if ( (packet_size + PLAYER_DATA_SLOP) > MAX_PACKET_SIZE ) {
1350 stop = APD_END_PACKET;
1352 multi_io_send_reliable( npp, data, packet_size );
1353 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1359 // add the stop byte
1360 stop = APD_END_DATA;
1362 multi_io_send_reliable(npp, data, packet_size);
1365 // send an accept packet to a client in response to a request to join the game
1366 void send_accept_packet(int new_player_num, int code, int ingame_join_team)
1369 ubyte data[MAX_PACKET_SIZE],val;
1370 char notify_string[256];
1373 Assert(new_player_num >= 0);
1375 // setup his "reliable" socket
1376 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1378 // build the packet header
1380 BUILD_HEADER(ACCEPT);
1382 // add the accept code
1385 // add code specific accept data
1386 if (code & ACCEPT_INGAME) {
1387 // the game filename
1388 ADD_STRING(Game_current_mission_filename);
1390 // if he is joining on a specific team, mark it here
1391 if(ingame_join_team != -1){
1394 val = (ubyte)ingame_join_team;
1402 if (code & ACCEPT_OBSERVER) {
1403 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1406 if (code & ACCEPT_HOST) {
1407 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1410 if (code & ACCEPT_CLIENT) {
1411 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1414 // add the current skill level setting on the host
1415 ADD_DATA(Game_skill_level);
1417 // add this guys player num
1418 ADD_DATA(new_player_num);
1420 // add his player id
1421 ADD_DATA(Net_players[new_player_num].player_id);
1423 // add netgame type flags
1424 ADD_DATA(Netgame.type_flags);
1427 // char buffer[100];
1428 // nprintf(("Network", "About to send accept packet to %s on port %d\n", get_text_address(buffer, addr->addr), addr->port ));
1431 // actually send the packet
1432 psnet_send(&Net_players[new_player_num].p_info.addr, data, packet_size);
1434 // if he's not an observer, inform all the other players in the game about him
1435 // inform the other players in the game about this new player
1436 for (i=0; i<MAX_PLAYERS; i++) {
1437 // skip unconnected players as well as this new guy himself
1438 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])) {
1442 // send the new packet
1443 send_new_player_packet(new_player_num,&Net_players[i]);
1446 // add a chat message
1447 if(Net_players[new_player_num].player->callsign != NULL){
1448 sprintf(notify_string,XSTR("<%s has joined>",717), Net_players[new_player_num].player->callsign);
1449 multi_display_chat_msg(notify_string, 0, 0);
1452 // handle any team vs. team details
1453 if (!(code & ACCEPT_OBSERVER)) {
1454 multi_team_handle_join(&Net_players[new_player_num]);
1458 if(Net_players[new_player_num].tracker_player_id >= 0){
1459 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);
1461 ml_printf(NOX("Server accepted %s as new client"), Net_players[new_player_num].player->callsign);
1466 // process the player data from the server
1467 void process_accept_player_data( ubyte *data, header *hinfo )
1469 int offset, player_num, player_slot_num, new_flags;
1470 char name[CALLSIGN_LEN + 1] = "";
1471 char image_name[MAX_FILENAME_LEN + 1] = "";
1472 char squad_name[MAX_FILENAME_LEN + 1] = "";
1473 char pxo_squad_name[LOGIN_LEN+1] = "";
1477 ushort ig_signature;
1479 offset = HEADER_LENGTH;
1482 while ( stop == APD_NEXT ) {
1483 player_slot_num = multi_find_open_player_slot();
1484 Assert(player_slot_num != -1);
1486 // get the player's number
1487 GET_DATA(player_num);
1489 // add the player's address
1492 // get the player's id#
1493 GET_DATA(player_id);
1498 // add his image filename
1499 GET_STRING(image_name);
1501 // get his squad logo filename
1502 GET_STRING(squad_name);
1504 // get his PXO squad name
1505 GET_STRING(pxo_squad_name);
1508 GET_DATA(new_flags);
1510 if (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) {
1511 if (!multi_obs_create_player(player_num, name, &addr, &Players[player_slot_num])) {
1516 // the error handling here is less than stellar. We should probably put up a popup and go
1517 // back to the main menu. But then again, this should never ever happen!
1518 if ( !multi_create_player(player_num, &Players[player_slot_num],name, &addr, -1, player_id) ) {
1523 // copy his image filename
1524 strcpy(Net_players[player_num].player->image_filename, image_name);
1526 // copy his pilot squad filename
1527 Net_players[player_num].player->insignia_texture = -1;
1528 player_set_squad_bitmap(Net_players[player_num].player, squad_name);
1530 // copy his pxo squad name
1531 strcpy(Net_players[player_num].p_info.pxo_squad_name, pxo_squad_name);
1533 // set his player id#
1534 Net_players[player_num].player_id = player_id;
1536 // mark him as being connected
1537 Net_players[player_num].flags |= NETINFO_FLAG_CONNECTED;
1538 Net_players[player_num].flags |= new_flags;
1540 // set the server pointer
1541 if ( Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER ) {
1542 Netgame.server = &Net_players[player_num];
1543 Netgame.server->last_heard_time = timer_get_fixed_seconds();
1545 // also - always set the server address to be where this data came from, NOT from
1546 // the data in the packet
1547 fill_net_addr(&Net_players[player_num].p_info.addr, hinfo->addr, hinfo->net_id, hinfo->port);
1550 // set the host pointer
1551 if ( Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST ) {
1552 Netgame.host = &Net_players[player_num];
1555 // read in the player's object net signature and store as his objnum for now
1556 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME ) {
1557 GET_DATA( ig_signature );
1558 Net_players[player_num].player->objnum = ig_signature;
1561 // get the stop byte
1566 if ( stop == APD_END_DATA ) {
1567 // if joining a game automatically, set the connect address to NULl so we don't try and
1568 // do this next time we enter a game
1569 if (Cmdline_connect_addr != NULL) {
1570 Cmdline_connect_addr = NULL;
1573 // send my stats to the server if I'm not in observer mode
1574 if (!(Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER)) {
1575 send_player_stats_block_packet(Net_player, STATS_ALLTIME);
1578 // if i'm being accepted as a host, then move into the host setup state
1579 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_HOST) {
1580 // set my permission bits
1581 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
1582 Net_player->state = NETPLAYER_STATE_STD_HOST_SETUP;
1584 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
1587 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER) {
1588 Net_player->flags |= NETINFO_FLAG_OBSERVER;
1590 // since observers can join 1 of 2 ways, only do this if we're not doing an ingame observer join
1591 if ( !(Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) ) {
1592 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1596 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_CLIENT) {
1597 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1600 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) {
1601 // flag myself as being an ingame joiner
1602 Net_player->flags |= NETINFO_FLAG_INGAME_JOIN;
1604 // move myself into the ingame join mission sync state
1605 Multi_sync_mode = MULTI_SYNC_INGAME;
1606 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
1609 // update my options on the server
1610 multi_options_update_local();
1612 // if we're in PXO mode, mark it down in our player struct
1613 if(MULTI_IS_TRACKER_GAME){
1614 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1615 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1620 // process an accept packet from the server
1621 extern int Select_default_ship;
1623 void process_accept_packet(ubyte* data, header* hinfo)
1625 int code, my_player_num, offset;
1629 // get the accept code
1630 offset = HEADER_LENGTH;
1634 // read in the accept code specific data
1636 if (code & ACCEPT_INGAME) {
1637 // the game filename
1638 GET_STRING(Game_current_mission_filename);
1639 Select_default_ship = 0;
1641 // determine if I'm being placed on a team
1648 if (code & ACCEPT_OBSERVER) {
1649 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1652 if (code & ACCEPT_HOST) {
1653 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1656 if (code & ACCEPT_CLIENT) {
1657 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1660 // fill in the netgame server address
1661 fill_net_addr( &Netgame.server_addr, hinfo->addr, hinfo->net_id, hinfo->port );
1663 // get the skill level setting
1664 GET_DATA(Game_skill_level);
1666 // get my netplayer number
1667 GET_DATA(my_player_num);
1670 GET_DATA(player_id);
1672 // get netgame type flags
1673 GET_DATA(Netgame.type_flags);
1675 // setup the Net_players structure for myself first
1676 Net_player = &Net_players[my_player_num];
1677 Net_player->flags = 0;
1678 Net_player->tracker_player_id = Multi_tracker_id;
1679 Net_player->player_id = player_id;
1680 Net_player->s_info.xfer_handle = -1;
1681 // stuff_netplayer_info( Net_player, &Psnet_my_addr, Ships[Objects[Player->objnum].instance].ship_info_index, Player );
1682 stuff_netplayer_info( Net_player, &Psnet_my_addr, 0, Player );
1683 multi_options_local_load(&Net_player->p_info.options, Net_player);
1685 Net_player->p_info.team = team;
1688 // determine if I have a CD
1690 Net_player->flags |= NETINFO_FLAG_HAS_CD;
1693 // set accept code in netplayer for this guy
1694 if ( code & ACCEPT_INGAME ){
1695 Net_player->flags |= NETINFO_FLAG_ACCEPT_INGAME;
1697 if ( code & ACCEPT_OBSERVER ){
1698 Net_player->flags |= NETINFO_FLAG_ACCEPT_OBSERVER;
1700 if ( code & ACCEPT_HOST ){
1701 Net_player->flags |= NETINFO_FLAG_ACCEPT_HOST;
1703 if ( code & ACCEPT_CLIENT ){
1704 Net_player->flags |= NETINFO_FLAG_ACCEPT_CLIENT;
1707 // if I have hacked data
1708 if(game_hacked_data()){
1709 Net_player->flags |= NETINFO_FLAG_HAXOR;
1712 // if we're supposed to flush our local data cache, do so now
1713 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
1714 multi_flush_multidata_cache();
1717 Net_player->sv_bytes_sent = 0;
1718 Net_player->sv_last_pl = -1;
1719 Net_player->cl_bytes_recvd = 0;
1720 Net_player->cl_last_pl = -1;
1722 // intiialize endgame stuff
1723 multi_endgame_init();
1727 // make a call to psnet to initialize and try to connect with the server.
1728 psnet_rel_connect_to_server( &Net_player->reliable_socket, &Netgame.server_addr );
1729 if ( Net_player->reliable_socket == INVALID_SOCKET ) {
1730 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONNECT_FAIL);
1734 // send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet)
1735 void send_leave_game_packet(short player_id, int kicked_reason, net_player *target)
1737 ubyte data[MAX_PACKET_SIZE];
1739 int packet_size = 0;
1741 BUILD_HEADER(LEAVE_GAME);
1743 // add a flag indicating whether he was kicked or not
1744 val = (char)kicked_reason;
1747 if (player_id < 0) {
1748 ADD_DATA(Net_player->player_id);
1750 // inform the host that we are leaving the game
1751 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1752 multi_io_send_to_all_reliable(data, packet_size);
1754 multi_io_send_reliable(Net_player, data, packet_size);
1757 // this is the case where to server is tossing a player (or indicating a respawned player has quit or become an observer)
1758 // so he has to tell everyone that this guy left
1760 nprintf(("Network","Sending a leave game packet to all players (server)\n"));
1762 // a couple of important checks
1763 Assert(player_id != Net_player->player_id);
1764 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1766 // add the id of the guy to be kicked
1767 ADD_DATA(player_id);
1769 // broadcast to everyone
1770 if (target == NULL) {
1771 multi_io_send_to_all_reliable(data, packet_size);
1773 multi_io_send_reliable(target, data, packet_size);
1778 // process a notification the a player has left the game
1779 void process_leave_game_packet(ubyte* data, header* hinfo)
1787 offset = HEADER_LENGTH;
1789 // get whether he was kicked
1790 GET_DATA(kicked_reason);
1792 // get the address of the guy who is to leave
1793 GET_DATA(deader_id);
1796 // determine who is dropping and printf out a notification
1797 player_num = find_player_id(deader_id);
1798 if (player_num == -1) {
1799 nprintf(("Network", "Received leave game packet for unknown player, ignoring\n"));
1803 nprintf(("Network", "Received a leave game notice for %s\n", Net_players[player_num].player->callsign));
1806 // a hook to display that a player was kicked
1807 if (kicked_reason >= 0){
1808 // if it was me that was kicked, leave the game
1809 if((Net_player != NULL) && (Net_player->player_id == deader_id)){
1812 switch(kicked_reason){
1813 case KICK_REASON_BAD_XFER:
1814 notify_code = MULTI_END_NOTIFY_KICKED_BAD_XFER;
1816 case KICK_REASON_CANT_XFER:
1817 notify_code = MULTI_END_NOTIFY_KICKED_CANT_XFER;
1819 case KICK_REASON_INGAME_ENDED:
1820 notify_code = MULTI_END_NOTIFY_KICKED_INGAME_ENDED;
1823 notify_code = MULTI_END_NOTIFY_KICKED;
1827 multi_quit_game(PROMPT_NONE, notify_code);
1830 // otherwise indicate someone was kicked
1832 nprintf(("Network","%s was kicked\n",Net_players[player_num].player->callsign));
1834 // display the result
1835 memset(str, 0, 512);
1836 multi_kick_get_text(&Net_players[player_num], kicked_reason, str);
1837 multi_display_chat_msg(str, player_num, 0);
1841 // first of all, if we're the master, we should be rebroadcasting this packet
1842 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1845 sprintf(msg, XSTR("%s has left the game",719), Net_players[player_num].player->callsign );
1847 if (!(Game_mode & GM_STANDALONE_SERVER)){
1848 HUD_sourced_printf(HUD_SOURCE_HIDDEN, msg);
1851 send_hud_msg_to_all(msg);
1852 multi_io_send_to_all_reliable(data, offset);
1855 // leave the game if the host and/or master has dropped
1857 if (((Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER) || (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)) ) {
1858 nprintf(("Network","Host and/or server has left the game - aborting...\n"));
1861 ml_string(NOX("Host and/or server has left the game"));
1863 // if the host leaves in the debriefing state, we should still wait until the player selects accept before we quit
1864 if (gameseq_get_state() != GS_STATE_DEBRIEF) {
1865 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
1868 delete_player(player_num);
1871 delete_player(player_num);
1873 // OSAPI GUI stuff (if standalone)
1874 if (Game_mode & GM_STANDALONE_SERVER) {
1875 // returns true if we should reset the standalone
1876 if (std_remove_player(&Net_players[player_num])) {
1877 nprintf(("Network", "Should reset!!\n"));
1881 // update these gui vals
1882 std_connect_set_host_connect_status();
1883 std_connect_set_connect_count();
1887 // send information about this currently active game to the specified address
1888 void send_game_active_packet(net_addr* addr)
1892 ubyte data[MAX_PACKET_SIZE],val;
1894 // build the header and add the data
1895 BUILD_HEADER(GAME_ACTIVE);
1897 // add the server version and compatible version #
1898 val = MULTI_FS_SERVER_VERSION;
1900 val = MULTI_FS_SERVER_COMPATIBLE_VERSION;
1903 ADD_STRING(Netgame.name);
1904 ADD_STRING(Netgame.mission_name);
1905 ADD_STRING(Netgame.title);
1906 val = (ubyte)multi_num_players();
1909 // add the proper flags
1911 if((Netgame.mode == NG_MODE_PASSWORD) || ((Game_mode & GM_STANDALONE_SERVER) && (multi_num_players() == 0) && (std_is_host_passwd()))){
1912 flags |= AG_FLAG_PASSWD;
1915 // proper netgame type flags
1916 if(Netgame.type_flags & NG_TYPE_TEAM){
1917 flags |= AG_FLAG_TEAMS;
1918 } else if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
1919 flags |= AG_FLAG_DOGFIGHT;
1921 flags |= AG_FLAG_COOP;
1924 // proper netgame state flags
1925 switch(Netgame.game_state){
1926 case NETGAME_STATE_FORMING:
1927 flags |= AG_FLAG_FORMING;
1930 case NETGAME_STATE_BRIEFING:
1931 case NETGAME_STATE_MISSION_SYNC:
1932 case NETGAME_STATE_HOST_SETUP:
1933 flags |= AG_FLAG_BRIEFING;
1936 case NETGAME_STATE_IN_MISSION:
1937 flags |= AG_FLAG_IN_MISSION;
1940 case NETGAME_STATE_PAUSED:
1941 flags |= AG_FLAG_PAUSE;
1944 case NETGAME_STATE_ENDGAME:
1945 case NETGAME_STATE_DEBRIEF:
1946 flags |= AG_FLAG_DEBRIEF;
1950 // if this is a standalone
1951 if(Game_mode & GM_STANDALONE_SERVER){
1952 flags |= AG_FLAG_STANDALONE;
1955 // if we're in campaign mode
1956 if(Netgame.campaign_mode == MP_CAMPAIGN){
1957 flags |= AG_FLAG_CAMPAIGN;
1960 // add the data about the connection speed of the host machine
1961 Assert( (Multi_connection_speed >= 0) && (Multi_connection_speed <= 4) );
1962 flags |= (Multi_connection_speed << AG_FLAG_CONNECTION_BIT);
1967 psnet_send(addr, data, packet_size);
1970 // process information about an active game
1971 void process_game_active_packet(ubyte* data, header* hinfo)
1976 int modes_compatible;
1978 fill_net_addr(&ag.server_addr, hinfo->addr, hinfo->net_id, hinfo->port);
1980 // read this game into a temporary structure
1981 offset = HEADER_LENGTH;
1983 // get the server version and compatible version
1984 GET_DATA(ag.version);
1985 GET_DATA(ag.comp_version);
1987 GET_STRING(ag.name);
1988 GET_STRING(ag.mission_name);
1989 GET_STRING(ag.title);
1991 ag.num_players = val;
1996 modes_compatible = 1;
1998 if((ag.flags & AG_FLAG_TRACKER) && !Multi_options_g.pxo){
1999 modes_compatible = 0;
2001 if(!(ag.flags & AG_FLAG_TRACKER) && Multi_options_g.pxo){
2002 modes_compatible = 0;
2006 // if this is a compatible version, and our modes are compatible, register it
2007 if( (ag.version == MULTI_FS_SERVER_VERSION) && modes_compatible ){
2008 multi_update_active_games(&ag);
2012 // send_game_update_packet sends an updated Netgame structure to all players currently connected. The update
2013 // is used to change the current mission, current state, etc.
2014 void send_netgame_update_packet(net_player *pl)
2018 ubyte data[MAX_PACKET_SIZE];
2021 BUILD_HEADER(GAME_UPDATE);
2023 // with new mission description field, this becomes way to large
2024 // so we must add every element piece by piece except the
2025 ADD_STRING(Netgame.name);
2026 ADD_STRING(Netgame.mission_name);
2027 ADD_STRING(Netgame.title);
2028 ADD_STRING(Netgame.campaign_name);
2029 ADD_DATA(Netgame.campaign_mode);
2030 ADD_DATA(Netgame.max_players);
2031 ADD_DATA(Netgame.security);
2032 ADD_DATA(Netgame.respawn);
2033 ADD_DATA(Netgame.flags);
2034 ADD_DATA(Netgame.type_flags);
2035 ADD_DATA(Netgame.version_info);
2036 ADD_DATA(Netgame.debug_flags);
2038 // only the server should ever send the netgame state (standalone situation)
2039 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2040 ADD_DATA(Netgame.game_state);
2043 // if we're the host on a standalone, send to the standalone and let him rebroadcast
2044 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2046 multi_io_send_to_all_reliable(data, packet_size);
2048 for(idx=0; idx<MAX_PLAYERS; idx++){
2049 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
2050 send_netgame_descript_packet(&Net_players[idx].p_info.addr, 1);
2054 multi_io_send_reliable(pl, data, packet_size);
2055 send_netgame_descript_packet( &pl->p_info.addr , 1 );
2058 Assert( pl == NULL ); // I don't think that a host in a standalone game would get here.
2059 multi_io_send_reliable(Net_player, data, packet_size);
2062 // host should always send a netgame options update as well
2063 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2064 multi_options_update_netgame();
2068 // process information about the netgame sent from the server/host
2069 void process_netgame_update_packet( ubyte *data, header *hinfo )
2071 int offset,old_flags;
2074 Assert(!(Game_mode & GM_STANDALONE_SERVER));
2075 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
2077 // read in the netgame information
2078 offset = HEADER_LENGTH;
2079 GET_STRING(Netgame.name);
2080 GET_STRING(Netgame.mission_name);
2081 GET_STRING(Netgame.title);
2082 GET_STRING(Netgame.campaign_name);
2083 GET_DATA(Netgame.campaign_mode);
2084 GET_DATA(Netgame.max_players); // ignore on the standalone, who keeps track of this himself
2085 GET_DATA(Netgame.security);
2086 GET_DATA(Netgame.respawn);
2088 // be sure not to blast the quitting flag because of the "one frame extra" problem
2089 old_flags = Netgame.flags;
2090 GET_DATA(Netgame.flags);
2091 GET_DATA(Netgame.type_flags);
2092 GET_DATA(Netgame.version_info);
2093 GET_DATA(Netgame.debug_flags);
2100 // now compare the passed in game state to our current known state. If it has changed, then maybe
2101 // do something interesting.
2102 // move from the forming or debriefing state to the mission sync state
2103 if ( ng_state == NETGAME_STATE_MISSION_SYNC ){
2104 // if coming from the forming state
2105 if( (Netgame.game_state == NETGAME_STATE_FORMING) ||
2106 ((Netgame.game_state != NETGAME_STATE_FORMING) && ((gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP) || (gameseq_get_state() == GS_STATE_MULTI_CLIENT_SETUP))) ){
2107 // do any special processing for forced state transitions
2108 multi_handle_state_special();
2110 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2111 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2112 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2114 // if coming from the debriefing state
2115 else if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2116 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2118 // do any special processing for forced state transitions
2119 multi_handle_state_special();
2121 multi_flush_mission_stuff();
2123 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2124 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2125 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2128 // move from mission sync to team select
2129 else if ( ng_state == NETGAME_STATE_BRIEFING ){
2130 if( (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ||
2131 ((Netgame.game_state != NETGAME_STATE_MISSION_SYNC) && (gameseq_get_state() == GS_STATE_MULTI_MISSION_SYNC) && (Multi_sync_mode != MULTI_SYNC_POST_BRIEFING)) ){
2133 // do any special processing for forced state transitions
2134 multi_handle_state_special();
2136 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2137 gameseq_post_event(GS_EVENT_START_BRIEFING);
2140 // move from the debriefing to the create game screen
2141 else if ( ng_state == NETGAME_STATE_FORMING ){
2142 if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2143 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2144 // do any special processing for forced state transitions
2145 multi_handle_state_special();
2147 multi_flush_mission_stuff();
2149 // move to the proper screen
2150 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2151 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2153 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
2158 Netgame.game_state = ng_state;
2161 // send a request or a reply for mission description, if code == 0, request, if code == 1, reply
2162 void send_netgame_descript_packet(net_addr *addr, int code)
2164 ubyte data[MAX_PACKET_SIZE],val;
2166 int packet_size = 0;
2169 BUILD_HEADER(UPDATE_DESCRIPT);
2175 // add as much of the description as we dare
2176 len = strlen(The_mission.mission_desc);
2177 if(len > MAX_PACKET_SIZE - 10){
2178 len = MAX_PACKET_SIZE - 10;
2180 memcpy(data+packet_size,The_mission.mission_desc,len);
2183 ADD_STRING(The_mission.mission_desc);
2187 Assert(addr != NULL);
2189 psnet_send(addr, data, packet_size);
2193 // process an incoming netgame description packet
2194 void process_netgame_descript_packet( ubyte *data, header *hinfo )
2198 char mission_desc[MISSION_DESC_LENGTH+2];
2201 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2203 // read this game into a temporary structure
2204 offset = HEADER_LENGTH;
2207 // if this is a request for mission description
2209 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2214 // send an update to this guy
2215 send_netgame_descript_packet(&addr, 1);
2217 memset(mission_desc,0,MISSION_DESC_LENGTH+2);
2218 GET_STRING(mission_desc);
2220 // only display if we're in the proper state
2221 state = gameseq_get_state();
2223 case GS_STATE_MULTI_JOIN_GAME:
2224 case GS_STATE_MULTI_CLIENT_SETUP:
2225 case GS_STATE_MULTI_HOST_SETUP:
2226 multi_common_set_text(mission_desc);
2234 // broadcast a query for active games. IPX will use net broadcast and TCP will either request from the MT or from the specified list
2235 void broadcast_game_query()
2239 server_item *s_moveup;
2240 ubyte data[MAX_PACKET_SIZE];
2242 BUILD_HEADER(GAME_QUERY);
2244 // go through the server list and query each of those as well
2245 s_moveup = Game_server_head;
2246 if(s_moveup != NULL){
2248 send_server_query(&s_moveup->server_addr);
2249 s_moveup = s_moveup->next;
2250 } while(s_moveup != Game_server_head);
2253 fill_net_addr(&addr, Psnet_my_addr.addr, Psnet_my_addr.net_id, DEFAULT_GAME_PORT);
2255 // send out a broadcast if our options allow us
2256 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2257 psnet_broadcast( &addr, data, packet_size);
2261 // send an individual query to an address to see if there is an active game
2262 void send_server_query(net_addr *addr)
2265 ubyte data[MAX_PACKET_SIZE];
2267 // build the header and send the data
2268 BUILD_HEADER(GAME_QUERY);
2269 psnet_send(addr, data, packet_size);
2272 // process a query from a client looking for active freespace games
2273 void process_game_query(ubyte* data, header* hinfo)
2278 offset = HEADER_LENGTH;
2282 // check to be sure that we don't capture our own broadcast message
2283 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2284 if ( psnet_same( &addr, &Psnet_my_addr) ){
2288 // if I am not a server of a game, don't send a reply!!!
2289 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
2293 // if the game options are being selected, then ignore the request
2294 // also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
2295 if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
2299 // send information about this active game
2300 send_game_active_packet(&addr);
2303 // sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
2304 void send_netplayer_update_packet( net_player *pl )
2306 int packet_size,idx;
2307 ubyte data[MAX_PACKET_SIZE],val;
2309 BUILD_HEADER(NETPLAYER_UPDATE);
2311 // if I'm the server of the game, I should send an update for _all_players in the game
2312 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2313 for(idx=0;idx<MAX_PLAYERS;idx++){
2314 // only send info for connected players
2315 if(MULTI_CONNECTED(Net_players[idx])){
2320 // add the net player's information
2321 ADD_DATA(Net_players[idx].player_id);
2322 ADD_DATA(Net_players[idx].state);
2323 ADD_DATA(Net_players[idx].p_info.ship_class);
2324 ADD_DATA(Net_players[idx].tracker_player_id);
2326 if(Net_players[idx].flags & NETINFO_FLAG_HAS_CD){
2334 // add the final stop byte
2338 // broadcast the packet
2339 if(!(Game_mode & GM_IN_MISSION)){
2341 multi_io_send_to_all_reliable(data, packet_size);
2343 multi_io_send_reliable(pl, data, packet_size);
2347 multi_io_send_to_all(data, packet_size);
2349 multi_io_send(pl, data, packet_size);
2357 // add my current state in the netgame to this packet
2358 ADD_DATA(Net_player->player_id);
2359 ADD_DATA(Net_player->state);
2360 ADD_DATA(Net_player->p_info.ship_class);
2361 ADD_DATA(Multi_tracker_id);
2363 // add if I have a CD or not
2371 // add a final stop byte
2375 // send the packet to the server
2376 Assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
2377 if(!(Game_mode & GM_IN_MISSION)){
2378 multi_io_send_reliable(Net_player, data, packet_size);
2380 multi_io_send(Net_player, data, packet_size);
2385 // process an incoming netplayer state update. if we're the server, we should rebroadcast
2386 void process_netplayer_update_packet( ubyte *data, header *hinfo )
2388 int offset, player_num;
2394 offset = HEADER_LENGTH;
2396 // get the first stop byte
2399 while(stop != 0xff){
2400 // look the player up
2401 GET_DATA(player_id);
2402 player_num = find_player_id(player_id);
2403 // if we couldn't find him, read in the bogus data
2404 if((player_num == -1) || (Net_player == &Net_players[player_num])){
2405 GET_DATA(bogus.state);
2406 GET_DATA(bogus.p_info.ship_class);
2407 GET_DATA(bogus.tracker_player_id);
2411 // otherwise read in the data correctly
2413 GET_DATA(new_state);
2414 GET_DATA(Net_players[player_num].p_info.ship_class);
2415 GET_DATA(Net_players[player_num].tracker_player_id);
2418 Net_players[player_num].flags |= NETINFO_FLAG_HAS_CD;
2420 Net_players[player_num].flags &= ~(NETINFO_FLAG_HAS_CD);
2423 // if he's changing state to joined, send a team update
2424 if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
2425 multi_team_send_update();
2429 Net_players[player_num].state = new_state;
2432 // get the next stop byte
2438 // if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
2439 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2440 send_netplayer_update_packet(NULL);
2443 // if i'm the standalone and this is an update from the host, maybe change some netgame settings
2444 if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
2445 switch(Net_players[player_num].state){
2446 case NETPLAYER_STATE_STD_HOST_SETUP:
2447 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2450 case NETPLAYER_STATE_HOST_SETUP:
2451 // check for race conditions
2452 if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
2453 Netgame.game_state = NETGAME_STATE_FORMING;
2460 #define EXTRA_DEATH_VAPORIZED (1<<0)
2461 #define EXTRA_DEATH_WASHED (1<<1)
2462 // send a packet indicating a ship has been killed
2463 void send_ship_kill_packet( object *objp, object *other_objp, float percent_killed, int self_destruct )
2465 int packet_size, model;
2466 ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
2467 ushort debris_signature;
2471 // only sendable from the master
2472 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
2475 vaporized = ( (Ships[objp->instance].flags & SF_VAPORIZE) > 0 );
2477 extra_death_info = 0;
2479 extra_death_info |= EXTRA_DEATH_VAPORIZED;
2482 if ( Ships[objp->instance].wash_killed ) {
2483 extra_death_info |= EXTRA_DEATH_WASHED;
2486 // find out the next network signature that will be used for the debris pieces.
2487 model = Ships[objp->instance].modelnum;
2488 pm = model_get(model);
2489 debris_signature = 0;
2490 if ( pm && !vaporized ) {
2491 debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
2492 multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
2493 Ships[objp->instance].arrival_distance = debris_signature;
2496 BUILD_HEADER(SHIP_KILL);
2497 ADD_DATA(objp->net_signature);
2499 // ships which are initially killed get the rest of the data sent. self destructed ships and
2500 if ( other_objp == NULL ) {
2505 nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
2507 ADD_DATA( other_objp->net_signature );
2510 ADD_DATA( debris_signature );
2511 ADD_DATA( percent_killed );
2512 sd = (ubyte)self_destruct;
2514 ADD_DATA( extra_death_info );
2516 // if the ship who died is a player, then send some extra info, like who killed him, etc.
2518 if ( objp->flags & OF_PLAYER_SHIP ) {
2522 pnum = multi_find_player_by_object( objp );
2525 ADD_DATA( was_player );
2527 Assert(Net_players[pnum].player->killer_objtype < CHAR_MAX);
2528 temp = (char)Net_players[pnum].player->killer_objtype;
2531 Assert(Net_players[pnum].player->killer_species < CHAR_MAX);
2532 temp = (char)Net_players[pnum].player->killer_species;
2535 Assert(Net_players[pnum].player->killer_weapon_index < CHAR_MAX);
2536 temp = (char)Net_players[pnum].player->killer_weapon_index;
2539 ADD_STRING( Net_players[pnum].player->killer_parent_name );
2541 ADD_DATA( was_player );
2544 ADD_DATA( was_player );
2547 // send the packet reliably!!!
2548 multi_io_send_to_all_reliable(data, packet_size);
2551 // process a packet indicating that a ship has been killed
2552 void process_ship_kill_packet( ubyte *data, header *hinfo )
2555 ushort ship_sig, other_sig, debris_sig;
2556 object *sobjp, *oobjp;
2557 float percent_killed;
2558 ubyte was_player, extra_death_info, sd;
2559 char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = SPECIES_TERRAN, killer_weapon_index = -1;
2561 offset = HEADER_LENGTH;
2564 GET_DATA( other_sig );
2565 GET_DATA( debris_sig );
2566 GET_DATA( percent_killed );
2568 GET_DATA( extra_death_info );
2569 GET_DATA( was_player );
2572 // pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
2573 if ( was_player != 0 ) {
2574 GET_DATA( killer_objtype );
2575 GET_DATA( killer_species );
2576 GET_DATA( killer_weapon_index );
2577 GET_STRING( killer_name );
2582 sobjp = multi_get_network_object( ship_sig );
2584 // if I am unable to find the ship object which was killed, I have to bail and rely on getting
2585 // another message from the server that this happened!
2586 if ( sobjp == NULL ) {
2587 nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
2591 // set this ship's hull value to 0
2592 sobjp->hull_strength = 0.0f;
2594 // maybe set vaporized
2595 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2596 Ships[sobjp->instance].flags |= SF_VAPORIZE;
2599 // maybe set wash_killed
2600 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2601 Ships[sobjp->instance].wash_killed = 1;
2604 oobjp = multi_get_network_object( other_sig );
2606 if ( was_player != 0 ) {
2609 pnum = multi_find_player_by_object( sobjp );
2611 Net_players[pnum].player->killer_objtype = killer_objtype;
2612 Net_players[pnum].player->killer_species = killer_species;
2613 Net_players[pnum].player->killer_weapon_index = killer_weapon_index;
2614 strcpy( Net_players[pnum].player->killer_parent_name, killer_name );
2618 // check to see if I need to respawn myself
2619 multi_respawn_check(sobjp);
2621 // store the debris signature in the arrival distance which will never get used for player ships
2622 Ships[sobjp->instance].arrival_distance = debris_sig;
2624 // set this bit so that we don't accidentally start switching targets when we die
2625 if(sobjp == Player_obj){
2626 Game_mode |= GM_DEAD_DIED;
2629 nprintf(("Network", "Killing off %s\n", Ships[sobjp->instance].ship_name));
2631 // do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
2632 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2633 ship_hit_kill( sobjp, oobjp, percent_killed, sd );
2635 extern void ship_destroyed( int shipnum );
2636 ship_destroyed( sobjp->instance );
2637 sobjp->flags |= OF_SHOULD_BE_DEAD;
2638 obj_delete( OBJ_INDEX(sobjp) );
2642 // send a packet indicating a ship should be created
2643 void send_ship_create_packet( object *objp, int is_support )
2646 ubyte data[MAX_PACKET_SIZE];
2648 // We will pass the ship to create by name.
2649 BUILD_HEADER(SHIP_CREATE);
2650 ADD_DATA(objp->net_signature);
2651 ADD_DATA( is_support );
2653 ADD_DATA( objp->pos );
2656 // broadcast the packet
2657 multi_io_send_to_all_reliable(data, packet_size);
2660 // process a packet indicating a ship should be created
2661 void process_ship_create_packet( ubyte *data, header *hinfo )
2663 int offset, objnum, is_support;
2666 vector pos = ZERO_VECTOR;
2668 Assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
2669 offset = HEADER_LENGTH;
2670 GET_DATA(signature);
2671 GET_DATA( is_support );
2678 // find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
2679 if ( !is_support ) {
2680 objp = mission_parse_get_arrival_ship( signature );
2681 if ( objp != NULL ) {
2682 objnum = parse_create_object(objp);
2684 nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
2687 Assert( Arriving_support_ship );
2688 if(Arriving_support_ship == NULL){
2691 Arriving_support_ship->pos = pos;
2692 Arriving_support_ship->net_signature = signature;
2693 objnum = parse_create_object( Arriving_support_ship );
2694 Assert( objnum != -1 );
2696 mission_parse_support_arrived( objnum );
2701 // send a packet indicating a wing of ships should be created
2702 void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
2704 int packet_size, index, ship_instance;
2705 ubyte data[MAX_PACKET_SIZE];
2709 // for creating wing -- we just send the index into the wing array of this wing.
2710 // all players load the same mission, and so their array's should all match. We also
2711 // need to send the signature of the first ship that was created. We can find this by
2712 // looking num_to_create places back in the ship_index field in the wing structure.
2714 index = WING_INDEX(wingp);
2715 ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
2716 signature = Objects[Ships[ship_instance].objnum].net_signature;
2718 BUILD_HEADER( WING_CREATE );
2720 ADD_DATA(num_to_create);
2721 ADD_DATA(signature);
2722 ADD_DATA(pre_create_count);
2723 val = wingp->current_wave - 1;
2726 multi_io_send_to_all_reliable(data, packet_size);
2729 // process a packet saying that a wing should be created
2730 void process_wing_create_packet( ubyte *data, header *hinfo )
2732 int offset, index, num_to_create;
2734 int total_arrived_count, current_wave;
2736 offset = HEADER_LENGTH;
2738 GET_DATA(num_to_create);
2739 GET_DATA(signature);
2740 GET_DATA(total_arrived_count);
2741 GET_DATA(current_wave);
2745 // do a sanity check on the wing to be sure that we are actually working on a valid wing
2746 if ( (index < 0) || (index >= num_wings) || (Wings[index].num_waves == -1) ) {
2747 nprintf(("Network", "invalid index %d for wing create packet\n"));
2750 if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
2751 nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
2756 Wings[index].current_count = 0;
2757 Wings[index].total_arrived_count = total_arrived_count;
2758 Wings[index].current_wave = current_wave;
2760 // set the network signature that was passed. The client should create ships in the same order
2761 // as the server -- so all ships should get the same sigs as assigned by the server. We also
2762 // need to set some timestamps and cues correctly to be sure that these things get created on
2763 // the clients correctly
2764 multi_set_network_signature( signature, MULTI_SIG_SHIP );
2765 parse_wing_create_ships( &Wings[index], num_to_create, 1 );
2768 // packet indicating a ship is departing
2769 void send_ship_depart_packet( object *objp )
2771 ubyte data[MAX_PACKET_SIZE];
2775 signature = objp->net_signature;
2777 BUILD_HEADER(SHIP_DEPART);
2778 ADD_DATA( signature );
2780 multi_io_send_to_all_reliable(data, packet_size);
2783 // process a packet indicating a ship is departing
2784 void process_ship_depart_packet( ubyte *data, header *hinfo )
2790 offset = HEADER_LENGTH;
2791 GET_DATA( signature );
2794 // find the object which is departing
2795 objp = multi_get_network_object( signature );
2796 if ( objp == NULL ) {
2797 nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
2801 // start warping him out
2802 shipfx_warpout_start( objp );
2805 // packet to tell clients cargo of a ship was revealed to all
2806 void send_cargo_revealed_packet( ship *shipp )
2808 ubyte data[MAX_PACKET_SIZE];
2811 // build the header and add the data
2812 BUILD_HEADER(CARGO_REVEALED);
2813 ADD_DATA( Objects[shipp->objnum].net_signature );
2815 // server sends to all players
2816 if(MULTIPLAYER_MASTER){
2817 multi_io_send_to_all_reliable(data, packet_size);
2819 // clients just send to the server
2821 multi_io_send_reliable(Net_player, data, packet_size);
2825 // process a cargo revealed packet
2826 void process_cargo_revealed_packet( ubyte *data, header *hinfo )
2832 offset = HEADER_LENGTH;
2833 GET_DATA(signature);
2836 // get a ship pointer and call the ship function to reveal the cargo
2837 objp = multi_get_network_object( signature );
2838 if ( objp == NULL ) {
2839 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
2843 // Assert( objp->type == OBJ_SHIP );
2844 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2848 // this will take care of re-routing to all other clients
2849 ship_do_cargo_revealed( &Ships[objp->instance], 1);
2851 // server should rebroadcast
2852 if(MULTIPLAYER_MASTER){
2853 send_cargo_revealed_packet(&Ships[objp->instance]);
2857 // defines used for secondary fire packet
2858 #define SFPF_ALLOW_SWARM (1<<7)
2859 #define SFPF_DUAL_FIRE (1<<6)
2860 #define SFPF_TARGET_LOCKED (1<<5)
2862 // send a packet indicating a secondary weapon was fired
2863 void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm )
2865 int packet_size, net_player_num;
2866 ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
2868 ushort target_signature;
2872 // Assert ( starting_count < UCHAR_MAX );
2874 // get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
2875 // we might send the info to the other player different than the one who fired
2876 objp = &Objects[shipp->objnum];
2877 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
2878 if ( num_fired == 0 ) {
2883 aip = &Ai_info[shipp->ai_index];
2885 current_bank = (ubyte)shipp->weapons.current_secondary_bank;
2886 Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) );
2888 // build up the header portion
2889 BUILD_HEADER( SECONDARY_FIRED_AI );
2891 ADD_DATA( Objects[shipp->objnum].net_signature );
2892 ADD_DATA( starting_sig );
2894 // add a couple of bits for swarm missiles and dual fire secondary weaspons
2897 sinfo = current_bank;
2900 sinfo |= SFPF_ALLOW_SWARM;
2903 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ){
2904 sinfo |= SFPF_DUAL_FIRE;
2907 if ( aip->current_target_is_locked ){
2908 sinfo |= SFPF_TARGET_LOCKED;
2913 // add the ship's target and any targeted subsystem
2914 target_signature = 0;
2916 if ( aip->target_objnum != -1) {
2917 target_signature = Objects[aip->target_objnum].net_signature;
2918 if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
2921 s_index = ship_get_index_from_subsys( aip->targeted_subsys, aip->target_objnum );
2922 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
2923 t_subsys = (char)s_index;
2926 if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
2927 Assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags & WIF_BOMB);
2932 ADD_DATA( target_signature );
2933 ADD_DATA( t_subsys );
2935 // just send this packet to everyone, then bail if an AI ship fired.
2936 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
2937 multi_io_send_to_all(data, packet_size);
2941 net_player_num = multi_find_player_by_object( objp );
2943 // getting here means a player fired. Send the current packet to all players except the player
2944 // who fired. If nothing got fired, then don't send to the other players -- we will just send
2945 // a packet to the player who will find out that he didn't fire anything
2946 if ( num_fired > 0 ) {
2947 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
2950 // if I (the master) fired, then return
2951 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
2955 // now build up the packet to send to the player who actually fired.
2956 BUILD_HEADER( SECONDARY_FIRED_PLR );
2957 ADD_DATA(starting_sig);
2960 // add the targeting information so that the player's weapons will always home on the correct
2962 ADD_DATA( target_signature );
2963 ADD_DATA( t_subsys );
2965 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
2968 /// process a packet indicating a secondary weapon was fired
2969 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
2971 int offset, allow_swarm, target_objnum_save;
2972 ushort net_signature, starting_sig, target_signature;
2973 ubyte sinfo, current_bank;
2974 object* objp, *target_objp;
2978 ship_subsys *targeted_subsys_save;
2980 offset = HEADER_LENGTH; // size of the header
2982 // if from_player is false, it means that the secondary weapon info in this packet was
2983 // fired by an ai object (or another player). from_player == 1 means tha me (the person
2984 // receiving this packet) fired the secondary weapon
2985 if ( !from_player ) {
2986 GET_DATA( net_signature );
2987 GET_DATA( starting_sig );
2988 GET_DATA( sinfo ); // are we firing swarm missiles
2990 GET_DATA( target_signature );
2991 GET_DATA( t_subsys );
2995 // find the object (based on network signatures) for the object that fired
2996 objp = multi_get_network_object( net_signature );
2997 if ( objp == NULL ) {
2998 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3002 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3003 // of the next fire time of this bank to 0 so we can fire right away
3004 shipp = &Ships[objp->instance];
3007 GET_DATA( starting_sig );
3010 GET_DATA( target_signature );
3011 GET_DATA( t_subsys );
3015 // get the object and ship
3017 shipp = Player_ship;
3020 // check the allow swarm bit
3022 if ( sinfo & SFPF_ALLOW_SWARM ){
3026 // set the dual fire properties of the ship
3027 if ( sinfo & SFPF_DUAL_FIRE ){
3028 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3030 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3033 // determine whether current target is locked
3034 Assert( shipp->ai_index != -1 );
3035 aip = &Ai_info[shipp->ai_index];
3036 if ( sinfo & SFPF_TARGET_LOCKED ) {
3037 aip->current_target_is_locked = 1;
3039 aip->current_target_is_locked = 0;
3042 // find out the current bank
3043 current_bank = (ubyte)(sinfo & 0x3);
3044 Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) );
3045 shipp->weapons.current_secondary_bank = current_bank;
3047 // make it so we can fire this ship's secondary bank immediately!!!
3048 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3049 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3051 // set this ship's target and subsystem information. We will save and restore target and
3052 // targeted subsystem so that we do not accidentally change targets for this player or
3053 // any AI ships on his system.
3054 target_objnum_save = aip->target_objnum;
3055 targeted_subsys_save = aip->targeted_subsys;
3057 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3058 aip->target_objnum = -1;
3059 aip->targeted_subsys = NULL;
3061 target_objp = multi_get_network_object( target_signature );
3062 if ( target_objp != NULL ) {
3063 aip->target_objnum = OBJ_INDEX(target_objp);
3065 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3066 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3070 if ( starting_sig != 0 ){
3071 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3073 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3076 ship_fire_secondary( objp, allow_swarm );
3078 // restore targeted object and targeted subsystem
3079 aip->target_objnum = target_objnum_save;
3080 aip->targeted_subsys = targeted_subsys_save;
3083 // send a packet indicating a countermeasure was fired
3084 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3086 ubyte data[MAX_PACKET_SIZE];
3091 Assert ( cmeasure_count < UCHAR_MAX );
3092 BUILD_HEADER(COUNTERMEASURE_FIRED);
3093 ADD_DATA( objp->net_signature );
3094 ADD_DATA( rand_val );
3096 multi_io_send_to_all(data, packet_size);
3099 // process a packet indicating a countermeasure was fired
3100 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3102 int offset, rand_val;
3108 offset = HEADER_LENGTH;
3110 GET_DATA( signature );
3111 GET_DATA( rand_val );
3114 objp = multi_get_network_object( signature );
3115 if ( objp == NULL ) {
3116 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3119 if(objp->type != OBJ_SHIP){
3122 // Assert ( objp->type == OBJ_SHIP );
3124 // make it so ship can fire right away!
3125 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3126 if ( objp == Player_obj ){
3127 nprintf(("network", "firing countermeasure from my ship\n"));
3130 ship_launch_countermeasure( objp, rand_val );
3133 // send a packet indicating that a turret has been fired
3134 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3137 ushort pnet_signature;
3138 ubyte data[MAX_PACKET_SIZE], cindex;
3145 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3149 // local setup -- be sure we are actually passing a weapon!!!!
3150 objp = &Objects[weapon_objnum];
3151 Assert ( objp->type == OBJ_WEAPON );
3152 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3156 pnet_signature = Objects[ship_objnum].net_signature;
3158 Assert( subsys_index < UCHAR_MAX );
3159 cindex = (ubyte)subsys_index;
3161 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3166 // build the fire turret packet.
3167 BUILD_HEADER(FIRE_TURRET_WEAPON);
3168 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.fvec);
3169 ADD_DATA( has_sig );
3170 ADD_DATA( pnet_signature );
3172 ADD_DATA( objp->net_signature );
3175 val = (short)ssp->submodel_info_1.angs.h;
3177 val = (short)ssp->submodel_info_2.angs.p;
3180 multi_io_send_to_all(data, packet_size);
3182 multi_rate_add(1, "tur", packet_size);
3185 // process a packet indicating a turret has been fired
3186 void process_turret_fired_packet( ubyte *data, header *hinfo )
3188 int offset, weapon_objnum, wid;
3189 ushort pnet_signature, wnet_signature;
3198 short pitch, heading;
3200 // get the data for the turret fired packet
3201 offset = HEADER_LENGTH;
3202 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3203 GET_DATA( has_sig );
3204 GET_DATA( pnet_signature );
3206 GET_DATA( wnet_signature );
3210 GET_DATA( turret_index );
3211 GET_DATA( heading );
3213 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3216 objp = multi_get_network_object( pnet_signature );
3217 if ( objp == NULL ) {
3218 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3222 // if this isn't a ship, do nothing
3223 if ( objp->type != OBJ_SHIP ){
3227 // make an orientation matrix from the o_fvec
3228 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3230 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3231 // hack, but should be suitable.
3232 shipp = &Ships[objp->instance];
3233 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3237 wid = ssp->system_info->turret_weapon_type;
3239 // bash the position and orientation of the turret
3240 ssp->submodel_info_1.angs.h = (float)heading;
3241 ssp->submodel_info_2.angs.p = (float)pitch;
3243 // get the world position of the weapon
3244 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3246 // create the weapon object
3247 if(wnet_signature != 0){
3248 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3250 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3251 if (weapon_objnum != -1) {
3252 if ( Weapon_info[wid].launch_snd != -1 ) {
3253 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3258 // send a mission log item packet
3259 void send_mission_log_packet( int num )
3262 ubyte data[MAX_PACKET_SIZE];
3267 Assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3269 // get the data from the log
3270 entry = &log_entries[num];
3271 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3272 sindex = (ushort)entry->index;
3274 BUILD_HEADER(MISSION_LOG_ENTRY);
3276 ADD_DATA(entry->flags);
3278 ADD_DATA(entry->timestamp);
3279 ADD_STRING(entry->pname);
3280 ADD_STRING(entry->sname);
3282 // broadcast the packet to all players
3283 multi_io_send_to_all_reliable(data, packet_size);
3286 // process a mission log item packet
3287 void process_mission_log_packet( ubyte *data, header *hinfo )
3292 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3295 Assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3297 offset = HEADER_LENGTH;
3301 GET_DATA(timestamp);
3307 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3310 // send a mission message packet
3311 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)
3314 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3316 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3317 Assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3318 Assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3320 up = (ubyte) priority;
3321 us = (ubyte) source;
3322 utime = (ubyte)timing;
3324 BUILD_HEADER(MISSION_MESSAGE);
3326 ADD_STRING(who_from);
3330 ADD_DATA(builtin_type);
3331 ADD_DATA(multi_team_filter);
3333 if (multi_target == -1){
3334 multi_io_send_to_all_reliable(data, packet_size);
3336 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3340 // process a mission message packet
3341 void process_mission_message_packet( ubyte *data, header *hinfo )
3343 int offset, id, builtin_type;
3344 ubyte priority, source, utiming;
3345 char who_from[NAME_LENGTH];
3346 int multi_team_filter;
3348 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3350 offset = HEADER_LENGTH;
3352 GET_STRING(who_from);
3356 GET_DATA(builtin_type);
3357 GET_DATA(multi_team_filter);
3361 // filter out builtin ones in TvT
3362 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3366 // maybe filter this out
3367 if(!message_filter_multi(id)){
3368 // send the message as if it came from an sexpression
3369 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3373 // just send them a pong back as fast as possible
3374 void process_ping_packet(ubyte *data, header *hinfo)
3379 offset = HEADER_LENGTH;
3382 // get the address to return the pong to
3383 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3389 // right now it just routes the pong through to the standalone gui, which is the only
3390 // system which uses ping and pong right now.
3391 void process_pong_packet(ubyte *data, header *hinfo)
3397 offset = HEADER_LENGTH;
3399 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3403 // if we're connected , see who sent us this pong
3404 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3405 lookup = find_player_id(hinfo->id);
3410 p = &Net_players[lookup];
3412 // evaluate the ping
3413 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3415 // put in calls to any functions which may want to know about the ping times from
3417 if(Game_mode & GM_STANDALONE_SERVER){
3418 std_update_player_ping(p);
3421 // mark his socket as still alive (extra precaution)
3422 psnet_mark_received(Net_players[lookup].reliable_socket);
3424 // otherwise, do any special processing
3426 // if we're in the join game state, see if this pong came from a server on our
3428 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3429 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3434 // send a ping packet
3435 void send_ping(net_addr *addr)
3437 unsigned char data[8];
3440 // build the header and send the packet
3441 BUILD_HEADER( PING );
3442 psnet_send(addr, &data[0], packet_size);
3445 // send a pong packet
3446 void send_pong(net_addr *addr)
3448 unsigned char data[8];
3451 // build the header and send the packet
3453 psnet_send(addr, &data[0], packet_size);
3456 // sent from host to master. give me the list of missions you have.
3457 // this will be used only in a standalone mode
3458 void send_mission_list_request( int what )
3460 ubyte data[MAX_PACKET_SIZE];
3463 // build the header and ask for a list of missions or campaigns (depending
3464 // on the 'what' flag).
3465 BUILD_HEADER(MISSION_REQUEST);
3467 multi_io_send_reliable(Net_player, data, packet_size);
3470 // maximum number of bytes that we can send in a mission items packet.
3471 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3473 // defines used to tell what type of packets are being sent
3474 #define MISSION_LIST_ITEMS 1
3475 #define CAMPAIGN_LIST_ITEMS 2
3477 // send an individual mission file item
3478 void send_mission_items( net_player *pl )
3480 ubyte data[MAX_PACKET_SIZE];
3485 BUILD_HEADER(MISSION_ITEM);
3487 // send the list of missions and campaigns avilable on the server. Stop when
3488 // reaching a certain maximum
3489 type = MISSION_LIST_ITEMS;
3491 for (i = 0; i < Multi_create_mission_count; i++ ) {
3495 ADD_STRING( Multi_create_mission_list[i].filename );
3496 ADD_STRING( Multi_create_mission_list[i].name );
3497 ADD_DATA( Multi_create_mission_list[i].flags );
3498 ADD_DATA( Multi_create_mission_list[i].max_players );
3499 ADD_DATA( Multi_create_mission_list[i].respawn );
3502 ADD_DATA( Multi_create_mission_list[i].valid_status );
3504 if ( packet_size > MAX_MISSION_ITEMS_BYTES ) {
3507 multi_io_send_reliable(pl, data, packet_size);
3508 BUILD_HEADER( MISSION_ITEM );
3514 multi_io_send_reliable(pl, data, packet_size);
3516 // send the campaign information
3517 type = CAMPAIGN_LIST_ITEMS;
3518 BUILD_HEADER(MISSION_ITEM);
3520 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3524 ADD_STRING( Multi_create_campaign_list[i].filename );
3525 ADD_STRING( Multi_create_campaign_list[i].name );
3526 ADD_DATA( Multi_create_campaign_list[i].flags );
3527 ADD_DATA( Multi_create_campaign_list[i].max_players );
3529 if ( packet_size > MAX_MISSION_ITEMS_BYTES ) {
3532 multi_io_send_reliable(pl, data, packet_size);
3533 BUILD_HEADER( MISSION_ITEM );
3539 multi_io_send_reliable(pl, data, packet_size);
3542 // process a request for a list of missions
3543 void process_mission_request_packet(ubyte *data, header *hinfo)
3545 int player_num,offset;
3547 offset = HEADER_LENGTH;
3550 // fill in the address information of where this came from
3551 player_num = find_player_id(hinfo->id);
3552 if(player_num == -1){
3553 nprintf(("Network","Could not find player to send mission list items to!\n"));
3557 send_mission_items( &Net_players[player_num] );
3560 // process an individual mission file item
3561 void process_mission_item_packet(ubyte *data,header *hinfo)
3564 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3565 ubyte stop, type,max_players;
3568 Assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3569 offset = HEADER_LENGTH;
3574 GET_STRING( filename );
3577 GET_DATA( max_players );
3579 // missions also have respawns and a crc32 associated with them
3580 if(type == MISSION_LIST_ITEMS){
3584 GET_DATA(valid_status);
3586 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3587 strcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename );
3588 strcpy(Multi_create_mission_list[Multi_create_mission_count].name, name );
3589 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3590 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3591 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3594 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3596 Multi_create_mission_count++;
3598 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3599 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3600 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename );
3601 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name );
3602 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3603 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3604 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3605 Multi_create_campaign_count++;
3614 // this will cause whatever list to get resorted (although they should be appearing in order)
3615 multi_create_setup_list_data(-1);
3618 // send a request to the server to pause or unpause the game
3619 void send_multi_pause_packet(int pause)
3621 ubyte data[MAX_PACKET_SIZE];
3623 int packet_size = 0;
3625 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3628 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3629 val = (ubyte) pause;
3631 // add the pause info
3634 // send the request to the server
3635 multi_io_send_reliable(Net_player, data, packet_size);
3638 // process a pause update packet (pause, unpause, etc)
3639 void process_multi_pause_packet(ubyte *data, header *hinfo)
3645 offset = HEADER_LENGTH;
3651 // get who sent the packet
3652 player_index = find_player_id(hinfo->id);
3653 // if we don't know who sent the packet, don't do anything
3654 if(player_index == -1){
3658 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3659 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3662 // send a game information update
3663 void send_game_info_packet()
3666 ubyte data[MAX_PACKET_SIZE], paused;
3668 // set the paused variable
3669 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3671 BUILD_HEADER(GAME_INFO);
3672 ADD_DATA( Missiontime );
3675 multi_io_send_to_all(data, packet_size);
3678 // process a game information update
3679 void process_game_info_packet( ubyte *data, header *hinfo )
3685 offset = HEADER_LENGTH;
3687 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3688 // time, set our time to server time (should take ping time into account!!!)
3689 GET_DATA( mission_time );
3694 // send an ingame nak packet
3695 void send_ingame_nak(int state, net_player *p)
3697 ubyte data[MAX_PACKET_SIZE];
3700 BUILD_HEADER(INGAME_NAK);
3704 multi_io_send_reliable(p, data, packet_size);
3707 // process an ingame nak packet
3708 void process_ingame_nak(ubyte *data, header *hinfo)
3710 int offset,state,pid;
3713 offset = HEADER_LENGTH;
3717 pid = find_player_id(hinfo->id);
3721 pl = &Net_players[pid];
3724 case ACK_FILE_ACCEPTED :
3725 Assert(Net_player->flags & NETINFO_FLAG_INGAME_JOIN);
3726 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3727 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3732 // send a packet telling players to end the mission
3733 void send_endgame_packet(net_player *pl)
3735 ubyte data[MAX_PACKET_SIZE];
3739 BUILD_HEADER(MISSION_END);
3741 // sending to a specific player?
3743 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3744 multi_io_send_reliable(pl, data, packet_size);
3748 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3749 // send all player stats here
3750 multi_broadcast_stats(STATS_MISSION);
3752 // if in dogfight mode, send all dogfight stats as well
3753 ml_string("Before dogfight stats!");
3754 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3755 ml_string("Sending dogfight stats!");
3757 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3759 ml_string("After dogfight stats!");
3761 // tell everyone to leave the game
3762 multi_io_send_to_all_reliable(data, packet_size);
3764 multi_io_send_reliable(Net_player, data, packet_size);
3768 // process a packet indicating we should end the current mission
3769 void process_endgame_packet(ubyte *data, header *hinfo)
3774 offset = HEADER_LENGTH;
3778 ml_string("Receiving endgame packet");
3780 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3781 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3782 // determine who this came from and make sure he is allowed to end the game
3783 player_num = find_player_id(hinfo->id);
3784 Assert(player_num != -1);
3789 // if the player is allowed to end the mission
3790 if(!multi_can_end_mission(&Net_players[player_num])){
3794 // act as if we hit alt+j locally
3795 multi_handle_end_mission_request();
3797 // all clients process immediately
3799 // ingame joiners should quit when they receive an endgame packet since the game is over
3800 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3801 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3805 // do any special processing for being in a state other than the gameplay state
3806 multi_handle_state_special();
3808 // make sure we're not already in the debrief state
3809 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3810 multi_warpout_all_players();
3815 // send a position/orientation update for myself (if I'm an observer)
3816 void send_observer_update_packet()
3818 ubyte data[MAX_PACKET_SIZE];
3823 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3824 // to send any update packets to anyone.
3825 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3829 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3835 BUILD_HEADER(OBSERVER_UPDATE);
3837 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3839 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3842 // add targeting infomation
3843 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3844 target_sig = Objects[Player_ai->target_objnum].net_signature;
3848 ADD_DATA(target_sig);
3850 multi_io_send(Net_player, data, packet_size);
3853 // process a position/orientation update from an observer
3854 void process_observer_update_packet(ubyte *data, header *hinfo)
3860 physics_info bogus_pi;
3863 offset = HEADER_LENGTH;
3865 obs_num = find_player_id(hinfo->id);
3867 memset(&bogus_pi,0,sizeof(physics_info));
3868 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
3870 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
3873 // targeting information
3874 GET_DATA(target_sig);
3877 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
3881 // set targeting info
3882 if(target_sig == 0){
3883 Net_players[obs_num].s_info.target_objnum = -1;
3885 target_obj = multi_get_network_object(target_sig);
3886 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
3889 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
3890 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
3891 Net_players[obs_num].s_info.eye_pos = g_vec;
3892 Net_players[obs_num].s_info.eye_orient = g_mat;
3895 void send_netplayer_slot_packet()
3897 ubyte data[MAX_PACKET_SIZE];
3898 int packet_size,idx;
3903 BUILD_HEADER(NETPLAYER_SLOTS_P);
3904 for(idx=0;idx<MAX_PLAYERS;idx++){
3905 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
3907 ADD_DATA(Net_players[idx].player_id);
3908 ADD_DATA(Objects[Net_players[idx].player->objnum].net_signature);
3909 ADD_DATA(Net_players[idx].p_info.ship_class);
3910 ADD_DATA(Net_players[idx].p_info.ship_index);
3916 // standalone case or not
3917 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3918 multi_io_send_to_all_reliable(data, packet_size);
3920 multi_io_send_reliable(Net_player, data, packet_size);
3924 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
3927 int player_num,ship_class,ship_index;
3933 offset = HEADER_LENGTH;
3935 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
3936 multi_untag_player_ships();
3940 GET_DATA(player_id);
3942 GET_DATA(ship_class);
3943 GET_DATA(ship_index);
3944 player_num = find_player_id(player_id);
3946 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
3948 // call the function in multiutil.cpp to set up the player object stuff
3949 // being careful not to muck with the standalone object
3950 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
3951 objp = multi_get_network_object(net_sig);
3952 Assert(objp != NULL);
3953 multi_assign_player_ship( player_num, objp, ship_class );
3954 Net_players[player_num].p_info.ship_index = ship_index;
3955 objp->flags &= ~(OF_COULD_BE_PLAYER);
3956 objp->flags |= OF_PLAYER_SHIP;
3963 // standalone should forward the packet and wait for a response
3964 if(Game_mode & GM_STANDALONE_SERVER){
3965 send_netplayer_slot_packet();
3968 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
3969 send_netplayer_update_packet();
3972 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
3973 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
3974 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
3975 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
3977 ubyte data[MAX_PACKET_SIZE], utmp;
3980 BUILD_HEADER(SHIP_WSTATE_CHANGE);
3981 ADD_DATA( Objects[shipp->objnum].net_signature );
3982 utmp = (ubyte)(what);
3984 utmp = (ubyte)(new_bank);
3986 utmp = (ubyte)(link_status);
3989 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
3990 multi_io_send_to_all_reliable(data, packet_size);
3993 void process_ship_weapon_change( ubyte *data, header *hinfo )
3997 ubyte what, new_bank, link_status;
4001 offset = HEADER_LENGTH;
4002 GET_DATA( signature );
4004 GET_DATA( new_bank );
4005 GET_DATA( link_status );
4008 objp = multi_get_network_object( signature );
4009 if ( objp == NULL ) {
4010 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4013 // Assert( objp->type == OBJ_SHIP );
4014 if(objp->type != OBJ_SHIP){
4018 // if this is my data, do nothing since I already have my own data
4019 if ( objp == Player_obj ){
4023 // now, get the ship and set the new bank and link modes based on the 'what' value
4024 shipp = &Ships[objp->instance];
4025 if ( what == MULTI_PRIMARY_CHANGED ) {
4026 shipp->weapons.current_primary_bank = new_bank;
4028 shipp->flags |= SF_PRIMARY_LINKED;
4030 shipp->flags &= ~SF_PRIMARY_LINKED;
4033 shipp->weapons.current_secondary_bank = new_bank;
4035 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4037 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4042 // ship status change procedure
4043 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4045 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4046 // for added redundancy.
4047 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4048 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4049 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4050 // back to the client. Also sends multiple times for redundancy
4051 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4052 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4053 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4055 #define SHIP_STATUS_REPEAT 2
4056 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4059 ubyte data[MAX_PACKET_SIZE];
4060 int packet_size = 0;
4066 BUILD_HEADER(SHIP_STATUS_CHANGE);
4068 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4069 temp = bi->status[idx];
4073 // server should send reliably (response packet)
4074 if(MULTIPLAYER_MASTER){
4075 multi_io_send_reliable(pl, data, packet_size);
4077 multi_io_send(pl, data, packet_size);
4081 void process_ship_status_packet(ubyte *data, header *hinfo)
4085 int player_num,unique_id;
4089 offset = HEADER_LENGTH;
4091 // zero out the button info structure for good measure
4092 memset(&bi,0,sizeof(button_info));
4094 // read the button-info
4095 GET_DATA(unique_id);
4097 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4099 bi.status[idx] = i_tmp;
4104 // this will be handled differently client and server side. Duh.
4105 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4106 // find which net-player has sent us butotn information
4107 player_num = find_player_id(hinfo->id);
4108 Assert(player_num >= 0);
4113 // don't process critical button information for observers
4114 // its a new button_info for this guy. apply and ack
4115 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4116 // mark that he's pressed this button
4117 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4119 // send a return packet
4120 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4122 // apply the button presses to his ship as normal
4123 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4125 // else ignore it as a repeat from the same guy
4126 } else { // CLIENT SIDE
4127 // this is the return from the server, so we should now apply them locally
4128 // if(lookup_ship_status(Net_player,unique_id,1)){
4129 multi_apply_ship_status(Net_player, &bi, 1);
4134 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4135 // for clients. Left all details to this function instead of higher level messaging
4137 void send_player_order_packet(int type, int index, int cmd)
4139 ubyte data[MAX_PACKET_SIZE];
4141 ushort target_signature;
4143 int packet_size = 0;
4145 BUILD_HEADER(PLAYER_ORDER_PACKET);
4148 ADD_DATA(val); // ship order or wing order, or message all fighters
4150 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4151 if ( val != SQUAD_MSG_ALL ){
4152 ADD_DATA(index); // net signature of target ship
4155 ADD_DATA(cmd); // the command itself
4158 target_signature = 0;
4159 if ( Player_ai->target_objnum != -1 ){
4160 target_signature = Objects[Player_ai->target_objnum].net_signature;
4163 ADD_DATA( target_signature );
4166 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4169 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4170 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
4171 t_subsys = (char)s_index;
4175 multi_io_send_reliable(Net_player, data, packet_size);
4178 // brief explanation :
4179 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4180 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4181 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4182 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4183 void process_player_order_packet(ubyte *data, header *hinfo)
4185 int offset, player_num, command, index = 0, tobjnum_save;
4186 ushort target_signature;
4187 char t_subsys, type;
4188 object *objp, *target_objp;
4191 ship_subsys *tsubsys_save, *targeted_subsys;
4193 Assert(MULTIPLAYER_MASTER);
4195 // packet values - its easier to read all of these in first
4197 offset = HEADER_LENGTH;
4200 if ( type != SQUAD_MSG_ALL ){
4204 GET_DATA( command );
4205 GET_DATA( target_signature );
4206 GET_DATA( t_subsys );
4210 player_num = find_player_id(hinfo->id);
4211 if(player_num == -1){
4212 nprintf(("Network","Received player order packet from unknown player\n"));
4216 objp = &Objects[Net_players[player_num].player->objnum];
4217 if ( objp->type != OBJ_SHIP ) {
4218 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4222 // HACK HACK HACK HACK HACK HACK
4223 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4224 // the data is BOGUS. All people should be able to to these things as well.
4225 if(command == REARM_REPAIR_ME_ITEM){
4226 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4228 } else if(command == ABORT_REARM_REPAIR_ITEM){
4229 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4233 // if this player is not allowed to do messaging, quit here
4234 if( !multi_can_message(&Net_players[player_num]) ){
4235 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4239 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4240 // then call them in.
4241 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4242 Assert( (index >= 0) && (index < Num_reinforcements) );
4243 hud_squadmsg_call_reinforcement(index, player_num);
4247 // set the player's ai information here
4248 shipp = &Ships[objp->instance];
4249 aip = &Ai_info[shipp->ai_index];
4251 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4252 target_objp = multi_get_network_object( target_signature );
4253 if ( target_objp == NULL ) {
4257 targeted_subsys = NULL;
4258 if ( t_subsys != -1 ) {
4259 Assert( target_objp != NULL );
4260 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4263 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4265 tobjnum_save = aip->target_objnum;
4266 tsubsys_save = aip->targeted_subsys;
4268 if ( target_objp ) {
4269 aip->target_objnum = OBJ_INDEX(target_objp);
4271 aip->target_objnum = -1;
4274 aip->targeted_subsys = targeted_subsys;
4276 if ( type == SQUAD_MSG_SHIP ) {
4277 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4278 } else if ( type == SQUAD_MSG_WING ) {
4279 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4280 } else if ( type == SQUAD_MSG_ALL ) {
4281 hud_squadmsg_send_to_all_fighters( command, player_num );
4284 Assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4285 aip->target_objnum = tobjnum_save;
4286 aip->targeted_subsys = tsubsys_save;
4289 // FILE SIGNATURE stuff :
4290 // there are 2 cases for file signature sending which are handled very differently
4291 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4292 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4293 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4294 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4295 // would have to keep track of up to potentially 14 other ack handles (ouch).
4296 void send_file_sig_packet(ushort sum_sig,int length_sig)
4298 ubyte data[MAX_PACKET_SIZE];
4299 int packet_size = 0;
4301 BUILD_HEADER(FILE_SIG_INFO);
4303 ADD_DATA(length_sig);
4305 multi_io_send_reliable(Net_player, data, packet_size);
4308 void process_file_sig_packet(ubyte *data, header *hinfo)
4313 offset = HEADER_LENGTH;
4315 // should only be received on the server-side
4316 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4319 GET_DATA(length_sig);
4321 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4324 void send_file_sig_request(char *file_name)
4326 ubyte data[MAX_PACKET_SIZE];
4327 int packet_size = 0;
4329 BUILD_HEADER(FILE_SIG_REQUEST);
4330 ADD_STRING(file_name);
4332 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4334 multi_io_send_to_all_reliable(data, packet_size);
4337 void process_file_sig_request(ubyte *data, header *hinfo)
4339 int offset = HEADER_LENGTH;
4341 // get the mission name
4342 GET_STRING(Netgame.mission_name);
4345 // set the current mission filename
4346 strcpy(Game_current_mission_filename,Netgame.mission_name);
4349 multi_get_mission_checksum(Game_current_mission_filename);
4351 if(!multi_endgame_ending()){
4352 // reply to the server
4353 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4357 // functions to deal with subsystems getting whacked
4358 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4360 ubyte data[MAX_PACKET_SIZE];
4363 vector tmp, local_hitpos;
4366 Assert ( index < UCHAR_MAX );
4367 uindex = (ubyte)(index);
4369 objp = &Objects[shipp->objnum];
4371 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4372 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4374 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4375 ADD_DATA( Objects[shipp->objnum].net_signature );
4377 ADD_DATA( local_hitpos );
4379 multi_io_send_to_all_reliable(data, packet_size);
4382 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4388 vector local_hit_pos, world_hit_pos;
4390 offset = HEADER_LENGTH;
4392 GET_DATA( signature );
4394 GET_DATA( local_hit_pos );
4396 // get the network object. process it if we find it.
4397 objp = multi_get_network_object( signature );
4398 if ( objp != NULL ) {
4400 ship_subsys *subsysp;
4402 // be sure we have a ship!!!
4403 // Assert ( objp->type == OBJ_SHIP );
4404 if(objp->type != OBJ_SHIP){
4409 shipp = &Ships[objp->instance];
4411 // call to get the pointer to the subsystem we should be working on
4412 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4413 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4414 vm_vec_add2( &world_hit_pos, &objp->pos );
4416 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4417 if ( objp == Player_obj ) {
4418 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4426 // packet to tell clients cargo of a ship was revealed to all
4427 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4429 ubyte data[MAX_PACKET_SIZE], uindex;
4432 Assert ( index < UCHAR_MAX );
4433 uindex = (ubyte)(index);
4435 // build the header and add the data
4436 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4437 ADD_DATA( Objects[shipp->objnum].net_signature );
4440 // server sends to all players
4441 if(MULTIPLAYER_MASTER){
4442 multi_io_send_to_all_reliable(data, packet_size);
4444 // clients just send to the server
4446 multi_io_send_reliable(Net_player, data, packet_size);
4450 // process a subsystem cargo revealed packet
4451 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4458 ship_subsys *subsysp;
4460 offset = HEADER_LENGTH;
4461 GET_DATA( signature );
4465 // get a ship pointer and call the ship function to reveal the cargo
4466 objp = multi_get_network_object( signature );
4467 if ( objp == NULL ) {
4468 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4472 // Assert( objp->type == OBJ_SHIP );
4473 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4477 shipp = &Ships[objp->instance];
4479 // call to get the pointer to the subsystem we should be working on
4480 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4481 if (subsysp == NULL) {
4482 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4486 // this will take care of re-routing to all other clients
4487 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4488 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4490 // server should rebroadcast
4491 if(MULTIPLAYER_MASTER){
4492 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4496 void send_netplayer_load_packet(net_player *pl)
4498 ubyte data[MAX_PACKET_SIZE];
4499 int packet_size = 0;
4501 BUILD_HEADER(LOAD_MISSION_NOW);
4502 ADD_STRING(Netgame.mission_name);
4505 multi_io_send_to_all_reliable(data, packet_size);
4507 multi_io_send_reliable(pl, data, packet_size);
4511 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4514 int offset = HEADER_LENGTH;
4519 strcpy(Netgame.mission_name,str);
4520 strcpy(Game_current_mission_filename,str);
4521 if(!Multi_mission_loaded){
4523 // MWA 2/3/98 -- ingame join changes!!!
4524 // everyone can go through the same mission loading path here!!!!
4525 nprintf(("Network","Loading mission..."));
4527 // notify everyone that I'm loading the mission
4528 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4529 send_netplayer_update_packet();
4531 // do the load itself
4532 game_start_mission();
4534 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4535 // code will remark the correct player ships
4536 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4537 multi_untag_player_ships();
4540 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4541 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4542 send_netplayer_update_packet();
4544 Multi_mission_loaded = 1;
4545 nprintf(("Network","Finished loading mission\n"));
4549 void send_jump_into_mission_packet(net_player *pl)
4551 ubyte data[MAX_PACKET_SIZE];
4552 int packet_size = 0;
4554 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4556 BUILD_HEADER(JUMP_INTO_GAME);
4558 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4559 // and possible other things.
4561 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4562 ADD_DATA(Netgame.game_state);
4568 multi_io_send_to_all_reliable(data, packet_size);
4570 // send to a specific player
4572 multi_io_send_reliable(pl, data, packet_size);
4576 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4578 int offset = HEADER_LENGTH;
4583 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4584 // the game could be paused, so ingame joiner needs to deal with it.
4585 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4587 Netgame.game_state = state;
4592 // handle any special processing for being in a weird substate
4593 multi_handle_state_special();
4595 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4596 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4597 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4598 multi_ingame_observer_finish();
4600 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4601 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4602 send_netplayer_update_packet();
4605 // start the mission!!
4606 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4607 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4608 gameseq_post_event(GS_EVENT_ENTER_GAME);
4609 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4610 send_netplayer_update_packet();
4614 extern int Player_multi_died_check;
4615 Player_multi_died_check = -1;
4617 // recalc all object pairs now
4618 extern void obj_reset_all_collisions();
4619 obj_reset_all_collisions();
4621 // display some cool text
4622 multi_common_add_text(XSTR("Received mission start\n",720),1);
4625 ml_string(NOX("Client received mission start from server - entering mission"));
4630 char *repair_text[] = {
4632 "REPAIR_INFO_BEGIN",
4634 "REPAIR_INFO_UPDATE",
4635 "REPAIR_INFO_QUEUE",
4636 "REPAIR_INFO_ABORT",
4637 "REPAIR_INFO_BROKEN",
4638 "REPAIR_INFO_WARP_ADD",
4639 "REPAIR_INFO_WARP_REMOVE",
4640 "REPAIR_INFO_ONWAY",
4641 "REPAIR_INFO_KILLED",
4642 "REPAIR_INFO_COMPLETE",
4647 // the following two routines deal with updating and sending information regarding players
4648 // rearming and repairing during the game. The process function calls the routines to deal with
4649 // setting flags and other interesting things.
4650 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4652 int packet_size = 0;
4653 ushort repaired_signature, repair_signature;
4654 ubyte data[MAX_PACKET_SIZE];
4657 // use the network signature of the destination object if there is one, -1 otherwise.
4658 // client will piece it all together
4659 repaired_signature = repaired_objp->net_signature;
4661 // the repair ship may be NULL here since it might have been destroyed
4662 repair_signature = 0;
4664 repair_signature = repair_objp->net_signature;
4667 BUILD_HEADER(CLIENT_REPAIR_INFO);
4670 ADD_DATA( repaired_signature );
4671 ADD_DATA( repair_signature );
4673 multi_io_send_to_all_reliable(data, packet_size);
4675 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));
4678 void process_repair_info_packet(ubyte *data, header *hinfo)
4680 int offset = HEADER_LENGTH;
4681 ushort repaired_signature, repair_signature;
4682 object *repaired_objp, *repair_objp;
4686 GET_DATA( repaired_signature );
4687 GET_DATA( repair_signature );
4690 repaired_objp = multi_get_network_object( repaired_signature );
4691 repair_objp = multi_get_network_object( repair_signature );
4693 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));
4695 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4699 if ( repaired_objp == NULL ) {
4700 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4704 // the hope is to simply call the routine in the ai code to set/unset flags
4705 // based on the code value and everything else should happen..I hope....
4706 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4708 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4710 // set the dock flags when repair begins. Prevents problem in lagging docking
4711 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4713 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4714 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4715 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4718 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4720 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4721 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4724 if ( code == REPAIR_INFO_WARP_ADD ){
4725 mission_warp_in_support_ship( repaired_objp );
4727 mission_remove_scheduled_repair( repaired_objp );
4732 // sends information updating clients on certain AI information that clients will
4733 // need to know about to keep HUD information up to date. objp is the object that we
4734 // are updating, and what is the type of stuff that we are updating.
4735 void send_ai_info_update_packet( object *objp, char what )
4738 ushort other_signature;
4739 ubyte data[MAX_PACKET_SIZE];
4741 ubyte dock_index, dockee_index;
4743 // Assert( objp->type == OBJ_SHIP );
4744 if(objp->type != OBJ_SHIP){
4747 aip = &Ai_info[Ships[objp->instance].ai_index];
4750 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4753 BUILD_HEADER( AI_INFO_UPDATE );
4754 ADD_DATA( objp->net_signature );
4757 // depending on the "what" value, we will send different information
4761 case AI_UPDATE_DOCK:
4762 // for docking ships, add the signature of the ship that we are docking with.
4763 Assert( aip->dock_objnum != -1 );
4764 other_signature = Objects[aip->dock_objnum].net_signature;
4765 dock_index = (ubyte)(aip->dock_index);
4766 dockee_index = (ubyte)(aip->dockee_index);
4767 ADD_DATA( other_signature );
4768 ADD_DATA(dock_index);
4769 ADD_DATA(dockee_index);
4772 case AI_UPDATE_UNDOCK:
4773 // for undocking ships, check the dock_objnum since we might or might not have it
4774 // depending on whether or not a ship was destroyed while we were docked.
4775 other_signature = 0;
4776 if ( aip->dock_objnum != -1 )
4777 other_signature = Objects[aip->dock_objnum].net_signature;
4778 ADD_DATA( other_signature );
4782 case AI_UPDATE_ORDERS: {
4785 // for orders, we only need to send a little bit of information here. Be sure that the
4786 // first order for this ship is active
4787 Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4788 ADD_DATA( aip->goals[0].ai_mode );
4789 ADD_DATA( aip->goals[0].ai_submode );
4791 if ( aip->goals[0].ship_name != NULL )
4792 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4794 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4795 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4796 if ( shipnum != -1 ) {
4797 Assert( Ships[shipnum].objnum != -1 );
4798 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4800 other_signature = 0;
4802 ADD_DATA( other_signature );
4804 // for docking, add the dock and dockee index
4805 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4806 Assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4807 Assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4808 dock_index = (ubyte)aip->goals[0].docker.index;
4809 dockee_index = (ubyte)aip->goals[0].dockee.index;
4810 ADD_DATA( dock_index );
4811 ADD_DATA( dockee_index );
4820 multi_rate_add(1, "aiu", packet_size);
4821 multi_io_send_to_all_reliable(data, packet_size);
4824 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4825 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4826 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4828 int offset = HEADER_LENGTH;
4830 ushort net_signature, other_net_signature;
4831 object *objp, *other_objp;
4834 ubyte dock_index = 0, dockee_index = 0;
4836 GET_DATA( net_signature ); // signature of the object that we are dealing with.
4837 GET_DATA( code ); // code of what we are doing.
4838 objp = multi_get_network_object( net_signature );
4840 nprintf(("Network", "Couldn't find object for ai update\n"));
4843 case AI_UPDATE_DOCK:
4844 GET_DATA( other_net_signature );
4845 GET_DATA( dock_index );
4846 GET_DATA( dockee_index );
4847 other_objp = multi_get_network_object( other_net_signature );
4849 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
4851 // if we don't have an object to work with, break out of loop
4852 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
4856 Assert( other_objp->type == OBJ_SHIP );
4857 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
4858 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
4860 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4861 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4863 ai_do_objects_docked_stuff( objp, other_objp );
4866 case AI_UPDATE_UNDOCK:
4867 GET_DATA( other_net_signature );
4868 other_objp = multi_get_network_object( other_net_signature );
4870 // if we don't have an object to work with, break out of loop
4874 ai_do_objects_undocked_stuff( objp, other_objp );
4877 case AI_UPDATE_ORDERS:
4879 GET_DATA( submode );
4880 GET_DATA( other_net_signature );
4881 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4882 GET_DATA(dock_index);
4883 GET_DATA(dockee_index);
4886 // be sure that we have a ship object!!!
4887 if ( !objp || (objp->type != OBJ_SHIP) )
4890 // set up the information in the first goal element of the object in question
4891 aip = &Ai_info[Ships[objp->instance].ai_index];
4892 aip->active_goal = 0;
4893 aip->goals[0].ai_mode = mode;
4894 aip->goals[0].ai_submode = submode;
4896 // for docking, add the dock and dockee index
4897 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4898 aip->dock_index = dock_index;
4899 aip->dockee_index = dockee_index;
4902 // get a shipname if we can.
4903 other_objp = multi_get_network_object( other_net_signature );
4904 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
4905 // get a pointer to the shipname in question. Use the ship_name value in the
4906 // ship. We are only using this for HUD display, so I think that using this
4907 // method will be fine.
4908 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
4910 // special case for destroy subsystem -- get the ai_info pointer to our target ship
4911 // so that we can properly set up what subsystem this ship is attacking.
4912 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
4913 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
4915 // if docking -- set the dock index and dockee index of this other ship
4916 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4917 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4918 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4925 Int3(); // this Int3() should be temporary
4926 nprintf(("Network", "Invalid code for ai update: %d\n", code));
4932 // tell the standalone to move into the MISSION_SYNC_STATE
4933 void send_mission_sync_packet(int mode,int start_campaign)
4935 ubyte data[MAX_PACKET_SIZE],is_campaign;
4936 int packet_size = 0;
4938 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
4940 // build the header and add the sync mode (pre or post briefing)
4941 BUILD_HEADER(MISSION_SYNC_DATA);
4944 // if this is a campaign game
4945 if(mode == MULTI_SYNC_PRE_BRIEFING){
4946 if(Game_mode & GM_CAMPAIGN_MODE){
4947 // add a byte indicating campaign mode
4949 ADD_DATA(is_campaign);
4951 // add a byte indicating if we should be starting a campaign or continuing it
4952 is_campaign = (ubyte)start_campaign;
4953 ADD_DATA(is_campaign);
4955 // add the campaign filename
4956 ADD_STRING(Netgame.campaign_name);
4958 // otherwise if this is a single mission
4960 // add a byte indicating single mission mode
4962 ADD_DATA(is_campaign);
4964 // add the mission filename
4965 ADD_STRING(Game_current_mission_filename);
4968 multi_io_send_reliable(Net_player, data, packet_size);
4971 // move into the MISSION_SYNC state when this is received
4972 // this packet is sent only from a game host to a standalone
4973 void process_mission_sync_packet(ubyte *data, header *hinfo)
4976 ubyte campaign_flag;
4977 int offset = HEADER_LENGTH;
4979 Assert(Game_mode & GM_STANDALONE_SERVER);
4981 // if this is a team vs team situation, lock the players send a final team update
4982 if(Netgame.type_flags & NG_TYPE_TEAM){
4983 multi_team_host_lock_all();
4984 multi_team_send_update();
4987 // get the sync mode (pre or post briefing)
4990 if(mode == MULTI_SYNC_PRE_BRIEFING){
4991 // get the flag indicating if this is a single mission or a campaign mode
4992 GET_DATA(campaign_flag);
4994 // get the flag indicating whether we should be starting a new campaign
4995 GET_DATA(campaign_flag);
4997 // get the campaign filename
4998 GET_STRING(Netgame.campaign_name);
5000 // either start a new campaign or continue on to the next mission in the current campaign
5002 multi_campaign_start(Netgame.campaign_name);
5004 multi_campaign_next_mission();
5007 // make sure we remove the campaign mode flag
5008 Game_mode &= ~(GM_CAMPAIGN_MODE);
5010 // get the single mission filename
5011 GET_STRING(Game_current_mission_filename);
5012 strcpy(Netgame.mission_name,Game_current_mission_filename);
5017 // set the correct mode and m ove into the state
5018 Multi_sync_mode = mode;
5019 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5022 // tell a player to merge his mission stats into his alltime stats
5023 void send_store_stats_packet(int accept)
5026 int packet_size = 0;
5028 BUILD_HEADER(STORE_MISSION_STATS);
5030 // add whether we're accepting or tossing
5031 val = (ubyte)accept;
5034 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5035 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5036 multi_io_send_to_all_reliable(data, packet_size);
5038 multi_io_send_reliable(Net_player, data, packet_size);
5042 void process_store_stats_packet(ubyte *data, header *hinfo)
5044 int offset = HEADER_LENGTH;
5050 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5051 if(Game_mode & GM_STANDALONE_SERVER){
5052 // rebroadcast the packet to all others in the game
5053 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5054 multi_io_send_to_all_reliable(data, offset);
5057 // all players should mark the stats as being accepted in the debriefing
5058 multi_debrief_stats_accept();
5060 // all players should mark the stats as being "tossed" in the debriefing
5061 multi_debrief_stats_toss();
5066 void send_debris_update_packet(object *objp,int code)
5068 ubyte data[MAX_PACKET_SIZE];
5070 int packet_size = 0;
5072 BUILD_HEADER(DEBRIS_UPDATE);
5073 ADD_DATA(objp->net_signature);
5077 // add any extra relevant data
5079 case DEBRIS_UPDATE_UPDATE:
5080 ADD_DATA(objp->pos); // add position
5081 ADD_ORIENT(objp->orient); // add orientation
5082 ADD_DATA(objp->phys_info.vel); // add velocity
5083 ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5086 multi_io_send_to_all(data, packet_size);
5089 void process_debris_update_packet(ubyte *data, header *hinfo)
5093 object bogus_object;
5095 int offset = HEADER_LENGTH;
5101 objp = multi_get_network_object(net_sig);
5103 objp = &bogus_object;
5107 // update the object
5108 case DEBRIS_UPDATE_UPDATE:
5109 GET_DATA(objp->pos);
5110 GET_ORIENT(objp->orient);
5111 GET_DATA(objp->phys_info.vel);
5112 GET_DATA(objp->phys_info.rotvel);
5114 // simply remove it (no explosion)
5115 case DEBRIS_UPDATE_REMOVE:
5116 if(objp != &bogus_object){
5117 Assert(objp->type == OBJ_DEBRIS);
5118 obj_delete(OBJ_INDEX(objp));
5122 case DEBRIS_UPDATE_NUKE:
5123 if(objp != &bogus_object)
5124 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5132 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)
5134 ubyte data[MAX_PACKET_SIZE];
5136 int packet_size = 0;
5138 BUILD_HEADER(WSS_REQUEST_PACKET);
5140 // add the request information
5141 ADD_DATA(player_id);
5142 ADD_DATA(from_slot);
5143 ADD_DATA(from_index);
5146 ADD_DATA(wl_ship_slot); // only used in weapons loadout
5147 ADD_DATA(ship_class);
5150 // a standard request
5152 multi_io_send_reliable(Net_player, data, packet_size);
5154 // being routed through the standalone to the host of the game
5156 Assert(Game_mode & GM_STANDALONE_SERVER);
5157 multi_io_send_reliable(p, data, packet_size);
5161 void process_wss_request_packet(ubyte *data, header *hinfo)
5163 int offset = HEADER_LENGTH;
5164 int from_slot,from_index;
5165 int to_slot,to_index;
5167 int wl_ship_slot,ship_class;
5171 // determine who this request is from
5172 GET_DATA(player_id);
5173 player_num = find_player_id(player_id);
5175 // read in the request data
5176 GET_DATA(from_slot);
5177 GET_DATA(from_index);
5180 GET_DATA(wl_ship_slot); // only used in weapons loadout
5181 GET_DATA(ship_class); // only used in multi team select
5185 Assert(player_num != -1);
5186 if(player_num == -1){
5190 // if we're the standalone, we have to route this packet to the host of the game
5191 if(Game_mode & GM_STANDALONE_SERVER){
5192 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5194 // otherwise we're the host and should process the request
5197 case WSS_WEAPON_SELECT :
5198 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5200 case WSS_SHIP_SELECT :
5201 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5209 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5211 ubyte data[MAX_PACKET_SIZE],team;
5212 int packet_size = 0;
5214 Assert(size <= (MAX_PACKET_SIZE - 10));
5216 BUILD_HEADER(WSS_UPDATE_PACKET);
5218 // add the team/pool # this is for
5219 team = (ubyte)team_num;
5222 // add the data block size
5225 // add the data itself
5226 memcpy(data + packet_size,wss_data,size);
5227 packet_size += size;
5229 // if we're also the master of the game (not on a standalone)
5230 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5231 multi_io_send_to_all_reliable(data, packet_size);
5233 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5235 multi_io_send_reliable(Net_player, data, packet_size);
5239 void process_wss_update_packet(ubyte *data, header *hinfo)
5242 int size,player_index,idx;
5243 int offset = HEADER_LENGTH;
5245 // get the team/pool #
5248 // get the data size
5251 // if we're the standalone, then we should be routing this data to all the other clients
5252 if(Game_mode & GM_STANDALONE_SERVER){
5257 // determine where this came from
5258 player_index = find_player_id(hinfo->id);
5259 Assert(player_index != -1);
5260 if(player_index < 0){
5264 // route the packet (don't resend it to the host)
5265 for(idx=0;idx<MAX_PLAYERS;idx++){
5266 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5267 multi_io_send_reliable(&Net_players[idx], data, offset);
5271 // set the proper pool pointers
5272 ss_set_team_pointers((int)team);
5274 // read in the block of data, and apply it to the weapons/ship pools
5275 offset += restore_wss_data(data + offset);
5278 // set the pool pointers back to my own team
5279 ss_set_team_pointers(Net_player->p_info.team);
5281 // sync the interface if this was for my own team
5282 if((int)team == Net_player->p_info.team){
5283 multi_ts_sync_interface();
5290 // function to send firing information from the client to the server once they reach
5291 // the final sync screen.
5292 void send_firing_info_packet()
5294 ubyte data[MAX_PACKET_SIZE];
5296 ubyte plinked, sdual;
5298 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5300 BUILD_HEADER(FIRING_INFO);
5301 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5302 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5303 ADD_DATA( plinked );
5306 multi_io_send_reliable(Net_player, data, packet_size);
5309 void process_firing_info_packet( ubyte *data, header *hinfo )
5311 int offset, player_num;
5312 ubyte plinked, sdual;
5315 // only the master of the game should be dealing with these packets
5316 Assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5318 offset = HEADER_LENGTH;
5319 GET_DATA( plinked );
5323 player_num = find_player_id(hinfo->id);
5325 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5329 // get the ship pointer for this player and set the flags accordingly.
5330 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5332 shipp->flags |= SF_PRIMARY_LINKED;
5334 shipp->flags &= ~SF_PRIMARY_LINKED;
5337 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5339 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5342 // packet to deal with changing status of mission goals. used to be sent every so often from server
5343 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5344 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5345 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5346 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5348 ubyte data[MAX_PACKET_SIZE];
5351 BUILD_HEADER(MISSION_GOAL_INFO);
5354 ADD_DATA(new_status);
5357 multi_io_send_to_all_reliable(data, packet_size);
5360 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5362 int offset, goal_num, new_status, valid;
5364 offset = HEADER_LENGTH;
5366 GET_DATA(new_status);
5370 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5371 if ( new_status != -1 ){
5372 mission_goal_status_change( goal_num, new_status );
5374 mission_goal_validation_change( goal_num, valid );
5378 void send_player_settings_packet(net_player *p)
5380 ubyte data[MAX_PACKET_SIZE];
5383 int packet_size = 0;
5386 BUILD_HEADER(PLAYER_SETTINGS);
5388 // add all the data for all the players
5390 for(idx=0;idx<MAX_PLAYERS;idx++){
5391 if(MULTI_CONNECTED(Net_players[idx])){
5393 ADD_DATA(Net_players[idx].player_id);
5395 // break the p_info structure by member, so we don't overwrite any absolute pointers
5396 // ADD_DATA(Net_players[idx].p_info);
5397 ADD_DATA(Net_players[idx].p_info.team);
5398 ADD_DATA(Net_players[idx].p_info.ship_index);
5399 ADD_DATA(Net_players[idx].p_info.ship_class);
5402 // add the stop byte
5406 // either broadcast the data or send to a specific player
5408 multi_io_send_to_all_reliable(data, packet_size);
5410 multi_io_send_reliable(p, data, packet_size);
5414 void process_player_settings_packet(ubyte *data, header *hinfo)
5416 int offset,player_num;
5417 net_player_info bogus,*ptr;
5421 offset = HEADER_LENGTH;
5423 // read in the data for all the players
5425 while(stop != 0xff){
5426 // lookup the player
5427 GET_DATA(player_id);
5428 player_num = find_player_id(player_id);
5430 // make sure this is a valid player
5431 if(player_num == -1){
5434 ptr = &Net_players[player_num].p_info;
5437 GET_DATA(ptr->team);
5438 GET_DATA(ptr->ship_index);
5439 GET_DATA(ptr->ship_class);
5446 // update the server with my new state
5447 // MWA -- 3/31/98 -- check for in mission instead of state.
5448 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5449 if( !(Game_mode & GM_IN_MISSION) ) {
5450 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5451 send_netplayer_update_packet();
5455 // display some cool text
5456 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5459 void send_deny_packet(net_addr *addr, int code)
5462 int packet_size = 0;
5464 // build the header and add the rejection code
5470 psnet_send(addr, data, packet_size);
5473 void process_deny_packet(ubyte *data, header *hinfo)
5477 // get the denial code
5478 offset = HEADER_LENGTH;
5482 // if there is already a dialog active, do nothing - who cares at this point.
5487 // display the appropriate dialog
5489 case JOIN_DENY_JR_STATE :
5490 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));
5492 case JOIN_DENY_JR_TRACKER_INVAL :
5493 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));
5495 case JOIN_DENY_JR_PASSWD :
5496 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5498 case JOIN_DENY_JR_CLOSED :
5499 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));
5501 case JOIN_DENY_JR_TEMP_CLOSED :
5502 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));
5504 case JOIN_DENY_JR_RANK_HIGH :
5505 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));
5507 case JOIN_DENY_JR_RANK_LOW :
5508 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));
5510 case JOIN_DENY_JR_DUP :
5511 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5513 case JOIN_DENY_JR_FULL :
5514 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5516 case JOIN_DENY_JR_BANNED :
5517 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5519 case JOIN_DENY_JR_NOOBS :
5520 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5522 case JOIN_DENY_JR_INGAME_JOIN :
5523 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));
5525 case JOIN_DENY_JR_BAD_VERSION :
5526 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));
5528 case JOIN_DENY_JR_TYPE :
5529 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5533 // call this so that the join request timestamp automatically expires when we hear back from the server
5534 multi_join_reset_join_stamp();
5537 // this packet will consist of
5538 // 1.) netplayer ship classes (85 bytes max)
5539 // 2.) ship weapon state data (241 bytes max)
5540 // 3.) player settings et. al. (133 bytes max)
5541 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5542 void send_post_sync_data_packet(net_player *p, int std_request)
5544 ubyte data[MAX_PACKET_SIZE], val;
5549 ushort sval, ship_ets;
5550 int idx, player_index;
5551 int packet_size = 0;
5555 BUILD_HEADER(POST_SYNC_DATA);
5557 // some header information for standalone packet routing purposes
5558 val = (ubyte)std_request;
5561 // the standalone has two situations
5562 // 1.) sending a request to the host to distribute this block of data
5563 // 2.) having recevied this block of data from the host, it redistributes it
5564 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5565 // case 1, send the request
5566 multi_io_send_reliable(Netgame.host, data, packet_size);
5569 // case 2 for the standalone is below (as normal)
5571 // otherwise build the data now
5573 // add all deleted ships
5574 val = (ubyte)Multi_ts_num_deleted;
5576 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5577 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5583 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5584 shipp = &Ships[Objects[so->objnum].instance];
5586 // don't process non player wing ships
5587 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5593 // # of ships - used multiple times in the packet
5594 val = (ubyte)ship_count;
5597 // add ship class information (85 bytes max)
5598 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5599 shipp = &Ships[Objects[so->objnum].instance];
5601 // don't process non player wing ships
5602 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5605 // add the net signature of the object for look up
5606 ADD_DATA( Objects[so->objnum].net_signature );
5608 // add the ship info index
5609 val = (ubyte)(shipp->ship_info_index);
5612 // add the ships's team select index
5613 val = (ubyte)shipp->ts_index;
5617 // add weapon state information for all starting ships (241 bytes max)
5618 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5619 shipp = &Ships[Objects[so->objnum].instance];
5621 // don't process non player wing ships
5622 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5625 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5627 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5628 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5629 if(player_index == -1){
5632 pl = &Net_players[player_index];
5636 // add the net signature and other weapon information
5637 ADD_DATA( Objects[so->objnum].net_signature );
5639 // add number of primary and secondary banks
5640 bval = (char)(shipp->weapons.num_primary_banks);
5642 bval = (char)(shipp->weapons.num_secondary_banks);
5645 // add weapon bank status
5646 bval = (char)(shipp->weapons.current_primary_bank);
5648 pl->s_info.cur_primary_bank = bval;
5650 // Assert(bval != -1);
5653 bval = (char)(shipp->weapons.current_secondary_bank);
5655 pl->s_info.cur_secondary_bank = bval;
5657 // Assert(bval != -1);
5660 // primary weapon info
5661 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5663 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5666 // secondary weapon info
5667 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5669 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5670 ADD_DATA(val_short);
5671 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5673 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5674 ADD_DATA(val_short);
5675 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5677 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5678 ADD_DATA(val_short);
5680 // send primary and secondary weapon link status
5682 if(shipp->flags & SF_PRIMARY_LINKED){
5684 pl->s_info.cur_link_status |= (1<<0);
5688 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5690 pl->s_info.cur_link_status |= (1<<1);
5694 // if this is a player ship add (1<<2)
5695 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5700 // add a ship ets value
5703 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5705 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5707 ship_ets |= ((ushort)shipp->engine_recharge_index);
5712 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5713 // or if I'm the server as well as the host, I should be sending this to all players
5714 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5715 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5718 multi_io_send_to_all_reliable(data, packet_size);
5720 // send to a specific player
5722 multi_io_send_reliable(p, data, packet_size);
5725 multi_io_send_reliable(Net_player, data, packet_size);
5732 multi_io_send_to_all_reliable(data, packet_size);
5734 // send to a specific player
5736 multi_io_send_reliable(p, data, packet_size);
5741 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5743 ubyte val, sinfo_index, ts_index;
5745 ushort net_sig, ship_ets, sval;
5749 int offset = HEADER_LENGTH;
5753 // packet routing information
5756 // 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
5757 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5760 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5761 multi_ts_create_wings();
5763 // send to the standalone through my socket
5764 send_post_sync_data_packet(Net_player);
5770 // add all deleted ships
5772 Multi_ts_num_deleted = (int)val;
5773 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5774 // get the ship's objnum
5777 objp = multi_get_network_object(sval);
5779 // delete the ship appropriately
5780 // mark the object as having been deleted
5781 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5784 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5785 obj_delete(Multi_ts_deleted_objnums[idx]);
5786 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5788 Multi_ts_num_deleted--;
5789 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5797 // process ship class information
5798 for(idx=0; idx<ship_count; idx++){
5799 // get the object's net signature
5801 GET_DATA(sinfo_index);
5804 // attempt to get the object
5805 objp = multi_get_network_object(net_sig);
5807 // make sure we found a ship
5808 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5810 // set the ship to be the right class
5811 change_ship_type(objp->instance,(int)sinfo_index);
5813 // set the ship's team select index
5814 Ships[objp->instance].ts_index = (int)ts_index;
5817 // process ship weapon state info
5818 for(idx=0; idx<ship_count; idx++){
5819 // get the object's net signature
5822 // attempt to get the object
5823 objp = multi_get_network_object(net_sig);
5825 // make sure we found a ship
5826 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5828 // get a pointer to the ship
5829 shipp = &Ships[objp->instance];
5831 // get number of primary and secondary banks;
5834 shipp->weapons.num_primary_banks = (int)b;
5838 shipp->weapons.num_secondary_banks = (int)b;
5840 // get bank selection info
5845 shipp->weapons.current_primary_bank = (int)b;
5851 shipp->weapons.current_secondary_bank = (int)b;
5853 // primary weapon info
5855 shipp->weapons.primary_bank_weapons[0] = (int)b;
5858 shipp->weapons.primary_bank_weapons[1] = (int)b;
5860 // secondary weapon info
5862 shipp->weapons.secondary_bank_weapons[0] = (int)b;
5863 GET_DATA(val_short);
5864 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
5867 shipp->weapons.secondary_bank_weapons[1] = (int)b;
5868 GET_DATA(val_short);
5869 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
5872 shipp->weapons.secondary_bank_weapons[2] = (int)b;
5873 GET_DATA(val_short);
5874 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
5881 shipp->flags |= SF_PRIMARY_LINKED;
5884 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5886 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
5887 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
5889 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
5891 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
5897 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
5899 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
5901 shipp->engine_recharge_index = (ship_ets & 0x000f);
5906 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
5907 send_netplayer_update_packet();
5909 // the standalone server will receive this packet from the host of the game, to be applied locally and
5910 // also to be rebroadcast.
5911 if(Game_mode & GM_STANDALONE_SERVER){
5912 // update player ets settings
5913 for(idx=0;idx<MAX_PLAYERS;idx++){
5914 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
5915 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
5919 send_post_sync_data_packet(NULL,0);
5923 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
5925 ubyte data[MAX_PACKET_SIZE],val;
5928 int packet_size = 0;
5931 BUILD_HEADER(WSS_SLOTS_DATA);
5933 // some header information for standalone packet routing purposes
5934 val = (ubyte)std_request;
5938 val = (ubyte)team_num;
5941 // add whether this is the final packet or not
5945 // the standalone has two situations
5946 // 1.) sending a request to the host to distribute this block of data
5947 // 2.) having recevied this block of data from the host, it redistributes it
5948 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
5949 // case 1, send the request
5950 multi_io_send_reliable(Netgame.host, data, packet_size);
5953 // case 2 for the standalone is below (as normal)
5955 // add all the slots
5956 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
5957 // add the ship class
5958 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
5962 for(i = 0;i<MAX_WL_WEAPONS;i++){
5963 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
5967 // add the weapon counts
5968 for(i = 0;i<MAX_WL_WEAPONS;i++){
5969 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
5970 ADD_DATA(val_short);
5974 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5975 // or if I'm the server as well as the host, I should be sending this to all players
5976 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5977 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5980 multi_io_send_to_all_reliable(data, packet_size);
5982 // send to a specific player
5984 multi_io_send_reliable(p, data, packet_size);
5987 multi_io_send_reliable(Net_player, data, packet_size);
5994 multi_io_send_to_all_reliable(data, packet_size);
5996 // send to a specific player
5998 multi_io_send_reliable(p, data, packet_size);
6003 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6005 ubyte val,team_num,final;
6007 int offset = HEADER_LENGTH;
6010 // packet routing information
6016 // get whether this is the final packet or not
6019 // 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
6020 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6023 // send to the standalone through my socket
6024 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6028 // read in all the slot data
6029 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6030 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6032 // get the ship class
6034 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6037 for(i = 0;i<MAX_WL_WEAPONS;i++){
6039 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6041 // check for signed/unsigned problems
6042 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6043 Wss_slots_teams[team_num][idx].wep[i] = -1;
6047 // get the weapon counts
6048 for(i = 0;i<MAX_WL_WEAPONS;i++){
6049 GET_DATA(val_short);
6050 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6055 // update my netplayer state if this is the final packet
6057 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6058 send_netplayer_update_packet();
6061 // the standalone server will receive this packet from the host of the game, to be applied locally and
6062 // also to be rebroadcast.
6063 if(Game_mode & GM_STANDALONE_SERVER){
6064 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6066 // add some mission sync text
6067 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6071 #define OBJ_VISIBILITY_DOT 0.6f
6073 // send and receive packets for shield explosion information
6074 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6077 ubyte data[MAX_PACKET_SIZE], utri_num;
6080 // Assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6082 Assert( tri_num < UCHAR_MAX );
6083 utri_num = (ubyte)tri_num;
6085 // for each player, determine if this object is behind the player -- if so, don't
6087 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6088 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6090 vector eye_to_obj_vec, diff, eye_pos;
6093 eye_pos = Net_players[i].s_info.eye_pos;
6094 eye_orient = Net_players[i].s_info.eye_orient;
6096 // check for same vectors
6097 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6098 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6102 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6103 dot = vm_vec_dot(&eye_orient.fvec, &eye_to_obj_vec);
6105 if ( dot < OBJ_VISIBILITY_DOT ){
6109 BUILD_HEADER(SHIELD_EXPLOSION);
6111 ADD_DATA( Objects[objnum].net_signature );
6114 multi_io_send(&Net_players[i], data, packet_size);
6119 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6121 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6128 // get the shield hit data
6129 offset = HEADER_LENGTH;
6130 GET_DATA(signature);
6132 //GET_DATA(hit_pos);
6135 // find the object with this signature. If found, then do a shield explosion
6136 objp = multi_get_network_object( signature );
6139 shield_info *shieldp;
6144 // given the tri num, find the local position which is the average of the
6145 // three vertices of the triangle affected. Use this average point as the hit
6147 // Assert( objp->type == OBJ_SHIP );
6148 if(objp->type != OBJ_SHIP){
6152 pm = model_get(Ships[objp->instance].modelnum);
6153 shieldp = &pm->shield;
6154 Assert( utri_num < shieldp->ntris );
6155 stri = shieldp->tris[utri_num];
6156 vm_vec_zero(&hit_pos);
6157 for ( i = 0; i < 3; i++ ) {
6158 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6160 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6161 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6165 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6168 ubyte data[MAX_PACKET_SIZE], val;
6170 int packet_size = 0;
6175 sc = &pl->player->stats;
6178 BUILD_HEADER(PLAYER_STATS);
6180 // add the player id
6181 ADD_DATA(pl->player_id);
6183 // add the byte indicating whether these stats are all-time or not
6184 val = (ubyte)stats_code;
6187 // kill information - alltime
6191 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6192 u_tmp = sc->kills[idx];
6195 // medal information
6196 for(idx=0;idx<NUM_MEDALS;idx++){
6197 i_tmp = sc->medals[idx];
6201 ADD_DATA(sc->score);
6203 ADD_DATA(sc->assists);
6204 ADD_DATA(sc->kill_count);
6205 ADD_DATA(sc->kill_count_ok);
6206 ADD_DATA(sc->p_shots_fired);
6207 ADD_DATA(sc->s_shots_fired);
6208 ADD_DATA(sc->p_shots_hit);
6209 ADD_DATA(sc->s_shots_hit);
6210 ADD_DATA(sc->p_bonehead_hits);
6211 ADD_DATA(sc->s_bonehead_hits);
6212 ADD_DATA(sc->bonehead_kills);
6214 ADD_DATA(sc->missions_flown);
6215 ADD_DATA(sc->flight_time);
6216 ADD_DATA(sc->last_flown);
6217 ADD_DATA(sc->last_backup);
6222 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6223 u_tmp = sc->m_okKills[idx];
6227 ADD_DATA(sc->m_score);
6228 ADD_DATA(sc->m_assists);
6229 ADD_DATA(sc->m_kill_count);
6230 ADD_DATA(sc->m_kill_count_ok);
6231 ADD_DATA(sc->mp_shots_fired);
6232 ADD_DATA(sc->ms_shots_fired);
6233 ADD_DATA(sc->mp_shots_hit);
6234 ADD_DATA(sc->ms_shots_hit);
6235 ADD_DATA(sc->mp_bonehead_hits);
6236 ADD_DATA(sc->ms_bonehead_hits);
6237 ADD_DATA(sc->m_bonehead_kills);
6238 ADD_DATA(sc->m_player_deaths);
6239 ADD_DATA(sc->m_medal_earned);
6242 case STATS_MISSION_KILLS:
6243 ADD_DATA(sc->m_kill_count);
6244 ADD_DATA(sc->m_kill_count_ok);
6245 ADD_DATA(sc->m_assists);
6248 case STATS_DOGFIGHT_KILLS:
6249 for(idx=0; idx<MAX_PLAYERS; idx++){
6250 u_tmp = sc->m_dogfight_kills[idx];
6253 ADD_DATA(sc->m_kill_count);
6254 ADD_DATA(sc->m_kill_count_ok);
6255 ADD_DATA(sc->m_assists);
6259 Assert(packet_size < MAX_PACKET_SIZE);
6261 // if we're a client, always send the data to the server
6262 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6263 multi_io_send_reliable(Net_player, data, packet_size);
6265 // otherwise do server specific stuff
6267 // send to a specific target
6269 multi_io_send_reliable(target, data, packet_size);
6271 // otherwise, send to everyone
6273 multi_io_send_to_all_reliable(data, packet_size);
6278 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6282 scoring_struct *sc,bogus;
6284 int offset = HEADER_LENGTH;
6288 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6290 // get the player who these stats are for
6291 GET_DATA(player_id);
6292 player_num = find_player_id(player_id);
6293 if (player_num == -1) {
6294 nprintf(("Network", "Couldn't find player for stats update!\n"));
6295 ml_string("Couldn't find player for stats update!\n");
6300 sc = &Net_players[player_num].player->stats;
6303 // get the stats code
6307 ml_string("Received STATS_ALLTIME\n");
6310 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6312 sc->kills[idx] = u_tmp;
6315 // read in the stats
6316 for (idx=0; idx<NUM_MEDALS; idx++) {
6318 sc->medals[idx] = i_tmp;
6321 GET_DATA(sc->score);
6323 GET_DATA(sc->assists);
6324 GET_DATA(sc->kill_count);
6325 GET_DATA(sc->kill_count_ok);
6326 GET_DATA(sc->p_shots_fired);
6327 GET_DATA(sc->s_shots_fired);
6328 GET_DATA(sc->p_shots_hit);
6329 GET_DATA(sc->s_shots_hit);
6330 GET_DATA(sc->p_bonehead_hits);
6331 GET_DATA(sc->s_bonehead_hits);
6332 GET_DATA(sc->bonehead_kills);
6334 GET_DATA(sc->missions_flown);
6335 GET_DATA(sc->flight_time);
6336 GET_DATA(sc->last_flown);
6337 GET_DATA(sc->last_backup);
6341 ml_string("Received STATS_MISSION\n");
6343 // kills - mission OK
6344 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6346 sc->m_okKills[idx] = u_tmp;
6349 GET_DATA(sc->m_score);
6350 GET_DATA(sc->m_assists);
6351 GET_DATA(sc->m_kill_count);
6352 GET_DATA(sc->m_kill_count_ok);
6353 GET_DATA(sc->mp_shots_fired);
6354 GET_DATA(sc->ms_shots_fired);
6355 GET_DATA(sc->mp_shots_hit);
6356 GET_DATA(sc->ms_shots_hit);
6357 GET_DATA(sc->mp_bonehead_hits);
6358 GET_DATA(sc->ms_bonehead_hits);
6359 GET_DATA(sc->m_bonehead_kills);
6360 GET_DATA(sc->m_player_deaths);
6361 GET_DATA(sc->m_medal_earned);
6364 case STATS_MISSION_KILLS:
6365 ml_string("Received STATS_MISSION_KILLS\n");
6367 GET_DATA(sc->m_kill_count);
6368 GET_DATA(sc->m_kill_count_ok);
6369 GET_DATA(sc->m_assists);
6372 case STATS_DOGFIGHT_KILLS:
6373 ml_string("Received STATS_DOGFIGHT_KILLS\n");
6374 if(player_num >= 0){
6375 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6377 for(idx=0; idx<MAX_PLAYERS; idx++){
6379 sc->m_dogfight_kills[idx] = u_tmp;
6380 if(player_num >= 0){
6381 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6384 GET_DATA(sc->m_kill_count);
6385 GET_DATA(sc->m_kill_count_ok);
6386 GET_DATA(sc->m_assists);
6391 // if I'm the server of the game, I should always rebroadcast these stats
6392 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6393 // make sure these are alltime stats
6394 Assert(val == STATS_ALLTIME);
6396 multi_broadcast_stats(STATS_ALLTIME);
6400 // called to create asteroid stuff
6401 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6404 ubyte data[MAX_PACKET_SIZE];
6405 ubyte packet_type, atype;
6409 if (relvec != NULL )
6412 BUILD_HEADER( ASTEROID_INFO );
6413 packet_type = ASTEROID_CREATE;
6415 Assert( asteroid_type < UCHAR_MAX );
6416 atype = (ubyte)asteroid_type;
6418 ADD_DATA( packet_type );
6419 ADD_DATA( parent_objp->net_signature );
6420 ADD_DATA( new_objp->net_signature );
6424 multi_io_send_to_all(data, packet_size);
6427 void send_asteroid_throw( object *objp )
6430 ubyte data[MAX_PACKET_SIZE], packet_type;
6432 BUILD_HEADER( ASTEROID_INFO );
6434 // this packet type is an asteroid throw
6435 packet_type = ASTEROID_THROW;
6436 ADD_DATA( packet_type );
6437 ADD_DATA( objp->net_signature );
6438 ADD_DATA( objp->pos );
6439 ADD_DATA( objp->phys_info.vel );
6441 multi_io_send_to_all(data, packet_size);
6444 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6447 ubyte data[MAX_PACKET_SIZE], packet_type;
6451 if ( hitpos != NULL )
6454 // build up an asteroid hit packet
6455 BUILD_HEADER( ASTEROID_INFO );
6456 packet_type = ASTEROID_HIT;
6457 ADD_DATA( packet_type );
6458 ADD_DATA( objp->net_signature );
6460 if(other_objp == NULL){
6461 ushort invalid_sig = 0xffff;
6462 ADD_DATA(invalid_sig);
6464 ADD_DATA( other_objp->net_signature );
6469 multi_io_send_to_all(data, packet_size);
6472 void process_asteroid_info( ubyte *data, header *hinfo )
6477 offset = HEADER_LENGTH;
6478 GET_DATA( packet_type );
6480 // based on the packet type, do something interesting with an asteroid!
6481 switch( packet_type ) {
6483 case ASTEROID_CREATE: {
6484 ushort psignature, signature;
6487 object *parent_objp;
6489 GET_DATA( psignature );
6490 GET_DATA( signature );
6494 // after getting the values, set the next network signature, and call the create sub function
6495 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6496 parent_objp = multi_get_network_object( psignature );
6497 if ( parent_objp ) {
6498 asteroid_sub_create( parent_objp, atype, &relvec );
6500 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6507 // asteroid throw packet -- asteroid has wrapped bounds
6508 case ASTEROID_THROW: {
6513 GET_DATA( signature );
6516 objp = multi_get_network_object( signature );
6518 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6522 objp->phys_info.vel = vel;
6523 objp->phys_info.desired_vel = vel;
6527 case ASTEROID_HIT: {
6528 ushort signature, osignature;
6529 object *objp, *other_objp;
6533 GET_DATA( signature );
6534 GET_DATA( osignature );
6538 objp = multi_get_network_object( signature );
6539 if(osignature == 0xffff){
6542 other_objp = multi_get_network_object( osignature );
6545 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6549 if ( IS_VEC_NULL(&hitpos) ){
6550 asteroid_hit( objp, other_objp, NULL, damage );
6552 asteroid_hit( objp, other_objp, &hitpos, damage );
6555 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6556 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6557 weapon_hit( other_objp, objp, &hitpos );
6570 void send_host_restr_packet(char *callsign,int code,int mode)
6572 ubyte data[MAX_PACKET_SIZE],val;
6573 int packet_size = 0;
6575 // build the header and add the opcode
6576 BUILD_HEADER(HOST_RESTR_QUERY);
6583 ADD_STRING(callsign);
6585 // if I'm the standalone server, I should be sending this to the game host
6586 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6587 multi_io_send_reliable(Netgame.host, data, packet_size);
6589 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6591 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6592 multi_io_send_reliable(Net_player, data, packet_size);
6596 void process_host_restr_packet(ubyte *data, header *hinfo)
6600 int offset = HEADER_LENGTH;
6602 // get the opcode and the callsign
6605 GET_STRING(callsign);
6608 // do code specific operations
6610 // query to the host from standalone
6612 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6614 // set the join mode
6615 Multi_join_restr_mode = mode;
6617 // set the timestamp
6618 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6620 // notify the host of the event
6621 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6622 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6625 // affirmative reply from the host to the standalone
6627 Assert(Game_mode & GM_STANDALONE_SERVER);
6629 // let the player join if the timestamp has not already elapsed on the server
6630 if(Multi_restr_query_timestamp != -1){
6631 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6637 Assert(Game_mode & GM_STANDALONE_SERVER);
6638 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6639 Multi_restr_query_timestamp = -1;
6644 void send_netgame_end_error_packet(int notify_code,int err_code)
6648 int packet_size = 0;
6650 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6651 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6653 // build the header and add the notification and error codes
6654 BUILD_HEADER(NETGAME_END_ERROR);
6655 code = (char)notify_code;
6657 code = (char)err_code;
6661 multi_io_send_to_all_reliable(data, packet_size);
6664 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6666 int offset = HEADER_LENGTH;
6667 char notify_code,error_code;
6669 // get the error and notification codes
6670 GET_DATA(notify_code);
6671 GET_DATA(error_code);
6675 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6678 // sends info that a countermeasure succeeded.
6679 void send_countermeasure_success_packet( int objnum )
6681 int pnum, packet_size;
6682 ubyte data[MAX_PACKET_SIZE];
6684 pnum = multi_find_player_by_object( &Objects[objnum] );
6686 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6690 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6691 multi_io_send(&Net_players[pnum], data, packet_size);
6694 // start the flashing of my hud gauge
6695 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6699 offset = HEADER_LENGTH;
6702 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6703 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6706 #define UPDATE_IS_PAUSED (1<<0)
6707 #define UPDATE_HULL_INFO (1<<1)
6709 void send_client_update_packet(net_player *pl)
6711 ubyte data[MAX_PACKET_SIZE],val;
6712 int packet_size = 0;
6715 BUILD_HEADER(CLIENT_UPDATE);
6719 // add the pause status
6720 if ( Multi_pause_status ) {
6721 val |= UPDATE_IS_PAUSED;
6722 } 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) ) {
6723 val |= UPDATE_HULL_INFO;
6724 Assert( Player_ship ); // I"d better have one of these!!!!
6729 // if paused, add the net address of the guy who paused
6730 if(val & UPDATE_IS_PAUSED){
6731 Assert(Multi_pause_pauser != NULL);
6732 ADD_DATA(Multi_pause_pauser->player_id);
6735 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6736 if ( val & UPDATE_HULL_INFO ) {
6738 ubyte percent, ns, threats;
6741 ship_subsys *subsysp;
6744 // get the object for the player
6745 Assert( pl->player->objnum != -1 );
6747 objp = &Objects[pl->player->objnum];
6749 Assert ( objp->type == OBJ_SHIP );
6750 shipp = &Ships[objp->instance];
6751 sip = &Ship_info[shipp->ship_info_index];
6753 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6754 // percentage value since that should be close enough
6755 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6759 percent = (ubyte)temp;
6761 ADD_DATA( percent );
6763 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6764 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6765 ADD_DATA( percent );
6768 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6769 // both machines. Added as percent since that number <= 100
6771 // also write out the number of subsystems. We do this because the client might not know
6772 // about the object he is getting data for. (i.e. he killed the object already).
6773 ns = (ubyte)sip->n_subsystems;
6776 // now the subsystems.
6777 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6778 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6779 ADD_DATA( percent );
6782 // compute the threats for this player. Only compute the threats if this player is actually
6783 // playing (i.e. he has a ship)
6784 hud_update_reticle( pl->player );
6785 threats = (ubyte)pl->player->threat_flags;
6786 ADD_DATA( threats );
6788 // add his energy level for guns
6789 ADD_DATA(shipp->weapon_energy);
6791 // add his secondary bank ammo
6792 ADD_DATA(shipp->weapons.num_secondary_banks);
6793 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6794 ADD_DATA(shipp->weapons.secondary_bank_ammo[i]);
6799 ADD_DATA(pl->sv_last_pl);
6801 // send the packet reliably to the player
6802 multi_io_send(pl, data, packet_size);
6805 void process_client_update_packet(ubyte *data, header *hinfo)
6810 int is_paused, have_hull_info;
6813 float weapon_energy;
6814 int offset = HEADER_LENGTH;
6816 // get the header byte containing useful information
6819 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6820 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6822 // if we are paused, get who paused
6825 player_index = find_player_id(pauser);
6826 if(player_index != -1){
6827 Multi_pause_pauser = &Net_players[player_index];
6829 Multi_pause_pauser = NULL;
6833 // if we have hull information, then read it in.
6834 if ( have_hull_info ) {
6838 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
6840 ship_subsys *subsysp;
6844 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6845 // percentage value since that should be close enough
6846 GET_DATA( hull_percent );
6848 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
6850 shield_percent[i] = ub_tmp;
6853 // get the data for the subsystems
6854 GET_DATA( n_subsystems );
6855 for ( i = 0; i < n_subsystems; i++ ){
6857 subsystem_percent[i] = ub_tmp;
6860 GET_DATA( threats );
6862 // add his energy level for guns
6863 GET_DATA(weapon_energy);
6865 // add his secondary bank ammo
6866 GET_DATA(ammo_count);
6867 for(i=0; i<ammo_count; i++){
6871 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
6872 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
6873 // UDP client update packets. Only read this info if I have a ship.
6874 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
6875 shipp = Player_ship;
6877 sip = &Ship_info[shipp->ship_info_index];
6879 val = hull_percent * sip->initial_hull_strength / 100.0f;
6880 objp->hull_strength = val;
6882 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6883 val = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
6884 objp->shields[i] = val;
6887 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
6888 // then don't read these in.
6889 if ( n_subsystems == sip->n_subsystems ) {
6891 n_subsystems = 0; // reuse this variable
6892 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6895 val = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
6896 subsysp->current_hits = val;
6898 // add the value just generated (it was zero'ed above) into the array of generic system types
6899 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
6900 Assert ( subsys_type < SUBSYSTEM_MAX );
6901 shipp->subsys_info[subsys_type].current_hits += val;
6905 ship_recalc_subsys_strength( shipp );
6907 shipp->weapon_energy = weapon_energy;
6908 for(i=0; i<ammo_count; i++){
6909 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
6912 // update my threat flags.
6913 // temporarily commented out until tested.
6914 Net_player->player->threat_flags = threats;
6921 if(Net_player != NULL){
6922 Net_player->cl_last_pl = pl;
6926 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
6927 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
6929 multi_pause_pause();
6931 multi_pause_unpause();
6936 void send_countdown_packet(int time)
6940 int packet_size = 0;
6942 // build the header and add the time
6943 BUILD_HEADER(COUNTDOWN);
6947 // if we're the server, we should broadcast to everyone
6948 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6949 multi_io_send_to_all_reliable(data, packet_size);
6951 // otherwise we'de better be a host sending to the standalone
6953 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6954 multi_io_send_reliable(Net_player, data, packet_size);
6958 void process_countdown_packet(ubyte *data, header *hinfo)
6960 int offset = HEADER_LENGTH;
6967 // if we're not in the post sync data screen, ignore it
6968 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
6972 // if we're the standalone, this should be a -1 telling us to start the countdown
6973 if(Game_mode & GM_STANDALONE_SERVER){
6974 Assert((int)time == -1);
6976 // start the countdown
6977 multi_sync_start_countdown();
6979 // otherwise if we're clients, just bash the countdown
6981 Multi_sync_countdown = (int)time;
6985 // packets for debriefing information
6986 void send_debrief_info( int stage_count[], int *stages[] )
6988 ubyte data[MAX_PACKET_SIZE];
6989 int packet_size, i, j;
6992 BUILD_HEADER(DEBRIEF_INFO);
6994 // add the data for the teams
6995 for ( i = 0; i < Num_teams; i++ ) {
6998 count = stage_count[i];
7000 for ( j = 0; j < count; j++ ) {
7001 i_tmp = stages[i][j];
7006 multi_io_send_to_all_reliable(data, packet_size);
7009 // process a debrief info packet from the server
7010 void process_debrief_info( ubyte *data, header *hinfo )
7013 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7016 offset = HEADER_LENGTH;
7017 for ( i = 0; i < Num_teams; i++ ) {
7021 stage_counts[i] = count;
7022 stages[i] = active_stages[i];
7023 for ( j = 0; j < count; j++ ) {
7025 active_stages[i][j] = i_tmp;
7030 // now that we have the stage data for the debriefing stages, call debrief function with the
7031 // data so that clients can now see the debriefing stuff. Do it only for my team.
7032 Assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7033 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7036 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7037 // sends homing_object and homing_subsystem to all clients.
7038 void send_homing_weapon_info( int weapon_num )
7040 ubyte data[MAX_PACKET_SIZE];
7043 object *homing_object;
7044 ushort homing_signature;
7047 wp = &Weapons[weapon_num];
7049 // be sure that this weapon object is a homing object.
7050 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7053 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7054 // homing signature.
7055 homing_signature = 0;
7056 homing_object = wp->homing_object;
7057 if ( homing_object != NULL ) {
7058 homing_signature = homing_object->net_signature;
7060 // get the subsystem index.
7062 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7065 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7066 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
7067 t_subsys = (char)s_index;
7071 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7072 ADD_DATA( Objects[wp->objnum].net_signature );
7073 ADD_DATA( homing_signature );
7074 ADD_DATA( t_subsys );
7076 multi_io_send_to_all(data, packet_size);
7079 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7080 // packet contains information for multiple weapons (like hornets).
7081 void process_homing_weapon_info( ubyte *data, header *hinfo )
7084 ushort weapon_signature, homing_signature;
7086 object *homing_object, *weapon_objp;
7089 offset = HEADER_LENGTH;
7091 // get the data for the packet
7092 GET_DATA( weapon_signature );
7093 GET_DATA( homing_signature );
7094 GET_DATA( h_subsys );
7097 // deal with changing this weapons homing information
7098 weapon_objp = multi_get_network_object( weapon_signature );
7099 if ( weapon_objp == NULL ) {
7100 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7103 Assert( weapon_objp->type == OBJ_WEAPON );
7104 wp = &Weapons[weapon_objp->instance];
7106 // be sure that we can find these weapons and
7107 homing_object = multi_get_network_object( homing_signature );
7108 if ( homing_object == NULL ) {
7109 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7113 if ( homing_object->type == OBJ_WEAPON ) {
7114 Assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7117 wp->homing_object = homing_object;
7118 wp->homing_subsys = NULL;
7119 wp->target_num = OBJ_INDEX(homing_object);
7120 wp->target_sig = homing_object->signature;
7121 if ( h_subsys != -1 ) {
7122 Assert( homing_object->type == OBJ_SHIP );
7123 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7126 if ( homing_object->type == OBJ_SHIP ) {
7127 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7131 void send_emp_effect(ushort net_sig, float intensity, float time)
7136 Assert(MULTIPLAYER_MASTER);
7138 // build the packet and add the opcode
7139 BUILD_HEADER(EMP_EFFECT);
7141 ADD_DATA(intensity);
7144 // send it to the player
7145 multi_io_send_to_all(data, packet_size);
7148 void process_emp_effect(ubyte *data, header *hinfo)
7150 float intensity, time;
7153 int offset = HEADER_LENGTH;
7155 // read in the EMP effect data
7157 GET_DATA(intensity);
7161 // try and find the object
7162 objp = multi_get_network_object(net_sig);
7163 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7164 // if i'm not an observer and I have a valid ship, play the EMP effect
7165 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7166 emp_start_local(intensity, time);
7169 // start the effect for the ship itself
7170 emp_start_ship(objp, intensity, time);
7174 // tells whether or not reinforcements are available
7175 void send_reinforcement_avail( int rnum )
7180 BUILD_HEADER(REINFORCEMENT_AVAIL);
7182 multi_io_send_to_all_reliable(data, packet_size);
7185 void process_reinforcement_avail( ubyte *data, header *hinfo )
7190 offset = HEADER_LENGTH;
7194 // sanity check for a valid reinforcement number
7195 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7196 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7200 void send_change_iff_packet(ushort net_signature, int new_team)
7202 ubyte data[MAX_PACKET_SIZE];
7203 int packet_size = 0;
7205 if(Net_player == NULL){
7208 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7212 // build the packet and add the data
7213 BUILD_HEADER(CHANGE_IFF);
7214 ADD_DATA(net_signature);
7217 // send to all players
7218 multi_io_send_to_all_reliable(data, packet_size);
7221 void process_change_iff_packet( ubyte *data, header *hinfo)
7223 int offset = HEADER_LENGTH;
7224 ushort net_signature;
7229 GET_DATA(net_signature);
7233 // lookup the object
7234 objp = multi_get_network_object(net_signature);
7235 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7236 Ships[objp->instance].team = new_team;
7240 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7242 int packet_size, objnum;
7243 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7246 net_player *ignore = NULL;
7248 // sanity checking for now
7249 Assert ( banks_fired <= 3 );
7251 // get an object pointer for this ship.
7252 objnum = shipp->objnum;
7253 objp = &Objects[objnum];
7255 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7256 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7260 // just in case nothing got fired
7261 if(banks_fired <= 0){
7265 // ubanks_fired = (ubyte)banks_fired;
7266 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7267 // Assert( current_bank <= 3 );
7269 // insert the current primary bank into this byte
7270 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7272 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7273 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7274 // ubanks_fired |= (1<<7);
7277 // determine if its a player ship and don't send to him if we're in "client firing" mode
7278 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7279 if(MULTIPLAYER_MASTER){
7280 np_index = multi_find_player_by_net_signature(objp->net_signature);
7281 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7282 ignore = &Net_players[np_index];
7286 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7287 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7288 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7289 // and server in sync w.r.t. weapon energy for player ship
7290 BUILD_HEADER( PRIMARY_FIRED_NEW );
7291 ADD_DATA(objp->net_signature);
7292 // ADD_DATA(ubanks_fired);
7294 // if I'm a server, broadcast to all players
7295 if(MULTIPLAYER_MASTER){
7296 multi_io_send_to_all(data, packet_size, ignore);
7299 multi_rate_add(1, "wfi", packet_size);
7301 // otherwise just send to the server
7303 multi_io_send(Net_player, data, packet_size);
7307 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7309 int offset; // linked;
7310 // ubyte banks_fired, current_bank;
7315 // read all packet info
7316 offset = HEADER_LENGTH;
7317 GET_DATA(shooter_sig);
7318 // GET_DATA(banks_fired);
7321 // find the object this fired packet is operating on
7322 objp = multi_get_network_object( shooter_sig );
7323 if ( objp == NULL ) {
7324 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7327 // if this object is not actually a valid ship, don't do anything
7328 if(objp->type != OBJ_SHIP){
7331 if(objp->instance < 0){
7334 shipp = &Ships[objp->instance];
7336 // get the link status of the primary banks
7338 // if ( banks_fired & (1<<7) ) {
7340 // banks_fired ^= (1<<7);
7343 // get the current primary bank
7344 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7345 // current_bank &= 0x3;
7346 // Assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7347 // shipp->weapons.current_primary_bank = current_bank;
7349 // strip off all remaining bits and just keep which banks were actually fired.
7350 // banks_fired &= 0x3;
7352 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7355 // shipp->flags &= ~SF_PRIMARY_LINKED;
7357 // shipp->flags |= SF_PRIMARY_LINKED;
7360 // if we're in client firing mode, ignore ones for myself
7361 if((Player_obj != NULL) && (Player_obj == objp)){
7365 ship_fire_primary( objp, 0, 1 );
7368 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7370 ubyte data[MAX_PACKET_SIZE];
7373 net_player *ignore = NULL;
7375 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7376 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7380 Assert ( cmeasure_count < UCHAR_MAX );
7381 BUILD_HEADER(COUNTERMEASURE_NEW);
7382 ADD_DATA( objp->net_signature );
7383 ADD_DATA( rand_val );
7385 nprintf(("Network","Sending NEW countermeasure packet!\n"));
7387 // determine if its a player ship and don't send to him if we're in "client firing" mode
7388 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7389 if(MULTIPLAYER_MASTER){
7390 np_index = multi_find_player_by_net_signature(objp->net_signature);
7391 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7392 ignore = &Net_players[np_index];
7396 // if I'm the server, send to all players
7397 if(MULTIPLAYER_MASTER){
7398 multi_io_send_to_all(data, packet_size, ignore);
7400 // otherwise send to the server
7402 multi_io_send(Net_player, data, packet_size);
7406 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7413 offset = HEADER_LENGTH;
7414 GET_DATA( signature );
7415 GET_DATA( rand_val );
7418 objp = multi_get_network_object( signature );
7419 if ( objp == NULL ) {
7420 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7423 if(objp->type != OBJ_SHIP){
7427 // if we're in client firing mode, ignore ones for myself
7428 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7429 if((Player_obj != NULL) && (Player_obj == objp)){
7433 // make it so ship can fire right away!
7434 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7435 if ( objp == Player_obj ){
7436 nprintf(("network", "firing countermeasure from my ship\n"));
7438 ship_launch_countermeasure( objp, rand_val );
7441 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7443 ubyte data[MAX_PACKET_SIZE];
7444 int packet_size = 0;
7448 // only the server should ever be doing this
7449 Assert(MULTIPLAYER_MASTER);
7451 // setup outgoing data
7452 Assert(shooter != NULL);
7453 Assert(turret != NULL);
7454 Assert(target != NULL);
7455 Assert(override != NULL);
7456 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7459 u_beam_info = (ubyte)beam_info_index;
7460 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7461 Assert(subsys_index >= 0);
7462 if(subsys_index < 0){
7467 BUILD_HEADER(BEAM_FIRED);
7468 ADD_DATA(shooter->net_signature);
7469 ADD_DATA(subsys_index);
7470 ADD_DATA(target->net_signature);
7471 ADD_DATA(u_beam_info);
7472 ADD_DATA((*override));
7474 // send to all clients
7475 multi_io_send_to_all_reliable(data, packet_size);
7478 void process_beam_fired_packet(ubyte *data, header *hinfo)
7481 ushort shooter_sig, target_sig;
7485 beam_fire_info fire_info;
7487 // only clients should ever get this
7488 Assert(MULTIPLAYER_CLIENT);
7490 // read in packet data
7491 offset = HEADER_LENGTH;
7492 GET_DATA(shooter_sig);
7493 GET_DATA(subsys_index);
7494 GET_DATA(target_sig);
7495 GET_DATA(u_beam_info);
7499 // lookup all relevant data
7500 fire_info.beam_info_index = (int)u_beam_info;
7501 fire_info.shooter = NULL;
7502 fire_info.target = NULL;
7503 fire_info.turret = NULL;
7504 fire_info.target_subsys = NULL;
7505 fire_info.beam_info_override = NULL;
7506 fire_info.shooter = multi_get_network_object(shooter_sig);
7507 fire_info.target = multi_get_network_object(target_sig);
7508 fire_info.beam_info_override = &b_info;
7509 fire_info.accuracy = 1.0f;
7510 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)){
7511 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7514 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7515 if(fire_info.turret == NULL){
7516 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7521 beam_fire(&fire_info);
7524 void send_sw_query_packet(ubyte code, char *txt)
7526 ubyte data[MAX_PACKET_SIZE];
7527 int packet_size = 0;
7529 // build the packet and add the code
7530 BUILD_HEADER(SW_STD_QUERY);
7532 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7536 // if I'm the host, send to standalone
7537 if(MULTIPLAYER_HOST){
7538 Assert(!MULTIPLAYER_MASTER);
7539 Assert(code == SW_STD_START);
7540 multi_io_send_reliable(Net_player, data, packet_size);
7542 // otherwise standalone sends back to host
7544 Assert(Game_mode & GM_STANDALONE_SERVER);
7545 Assert(code != SW_STD_START);
7546 Assert(Netgame.host != NULL);
7547 if(Netgame.host != NULL){
7548 multi_io_send_reliable(Netgame.host, data, packet_size);
7553 void process_sw_query_packet(ubyte *data, header *hinfo)
7557 void send_event_update_packet(int event)
7559 ubyte data[MAX_PACKET_SIZE];
7560 ushort u_event = (ushort)event;
7561 int packet_size = 0;
7563 // build the header and add the event
7564 BUILD_HEADER(EVENT_UPDATE);
7566 ADD_DATA(Mission_events[event].flags);
7567 ADD_DATA(Mission_events[event].formula);
7568 ADD_DATA(Mission_events[event].result);
7569 ADD_DATA(Mission_events[event].count);
7571 // send to all players
7572 multi_io_send_to_all_reliable(data, packet_size);
7575 void process_event_update_packet(ubyte *data, header *hinfo)
7577 int offset = HEADER_LENGTH;
7583 store_flags = Mission_events[u_event].flags;
7584 GET_DATA(Mission_events[u_event].flags);
7585 GET_DATA(Mission_events[u_event].formula);
7586 GET_DATA(Mission_events[u_event].result);
7587 GET_DATA(Mission_events[u_event].count);
7590 // went from non directive special to directive special
7591 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7592 mission_event_set_directive_special(u_event);
7594 // if we went directive special to non directive special
7595 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7596 mission_event_unset_directive_special(u_event);
7600 // weapon detonate packet
7601 void send_weapon_detonate_packet(object *objp)
7603 ubyte data[MAX_PACKET_SIZE];
7604 int packet_size = 0;
7607 Assert(MULTIPLAYER_MASTER);
7608 if(!MULTIPLAYER_MASTER){
7611 Assert(objp != NULL);
7616 // build the header and add the data
7617 BUILD_HEADER(WEAPON_DET);
7618 ADD_DATA(objp->net_signature);
7620 // send to all players
7621 multi_io_send_to_all(data, packet_size);
7624 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7627 int offset = HEADER_LENGTH;
7628 object *objp = NULL;
7630 // get the weapon signature
7634 // lookup the weapon
7635 objp = multi_get_network_object(net_sig);
7636 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7637 weapon_detonate(objp);
7641 // flak fired packet
7642 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7645 ushort pnet_signature;
7646 ubyte data[MAX_PACKET_SIZE], cindex;
7652 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7656 // local setup -- be sure we are actually passing a weapon!!!!
7657 objp = &Objects[weapon_objnum];
7658 Assert ( objp->type == OBJ_WEAPON );
7659 pnet_signature = Objects[ship_objnum].net_signature;
7661 Assert( subsys_index < UCHAR_MAX );
7662 cindex = (ubyte)subsys_index;
7664 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7669 // build the fire turret packet.
7670 BUILD_HEADER(FLAK_FIRED);
7671 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.fvec);
7672 ADD_DATA( pnet_signature );
7674 val = (short)ssp->submodel_info_1.angs.h;
7676 val = (short)ssp->submodel_info_2.angs.p;
7678 ADD_DATA( flak_range );
7680 multi_io_send_to_all(data, packet_size);
7682 multi_rate_add(1, "flk", packet_size);
7685 void process_flak_fired_packet(ubyte *data, header *hinfo)
7687 int offset, weapon_objnum, wid;
7688 ushort pnet_signature;
7696 short pitch, heading;
7699 // get the data for the turret fired packet
7700 offset = HEADER_LENGTH;
7701 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7702 GET_DATA( pnet_signature );
7703 GET_DATA( turret_index );
7704 GET_DATA( heading );
7706 GET_DATA( flak_range );
7707 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7710 objp = multi_get_network_object( pnet_signature );
7711 if ( objp == NULL ) {
7712 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7716 // if this isn't a ship, do nothing
7717 if ( objp->type != OBJ_SHIP ){
7721 // make an orientation matrix from the o_fvec
7722 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7724 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7725 // hack, but should be suitable.
7726 shipp = &Ships[objp->instance];
7727 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7731 wid = ssp->system_info->turret_weapon_type;
7732 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7736 // bash the position and orientation of the turret
7737 ssp->submodel_info_1.angs.h = (float)heading;
7738 ssp->submodel_info_2.angs.p = (float)pitch;
7740 // get the world position of the weapon
7741 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7743 // create the weapon object
7744 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7745 if (weapon_objnum != -1) {
7746 if ( Weapon_info[wid].launch_snd != -1 ) {
7747 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7750 // create a muzzle flash from a flak gun based upon firing position and weapon type
7751 flak_muzzle_flash(&pos, &dir, wid);
7753 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7754 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7758 #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);
7759 #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);
7761 // player pain packet
7762 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7764 ubyte data[MAX_PACKET_SIZE];
7767 int packet_size = 0;
7769 Assert(MULTIPLAYER_MASTER);
7770 if(!MULTIPLAYER_MASTER){
7778 // build the packet and add the code
7779 BUILD_HEADER(NETPLAYER_PAIN);
7780 windex = (ubyte)weapon_info_index;
7782 udamage = (ushort)damage;
7785 ADD_DATA((*hitpos));
7787 // send to the player
7788 multi_io_send(pl, data, packet_size);
7790 multi_rate_add(1, "pai", packet_size);
7793 void process_player_pain_packet(ubyte *data, header *hinfo)
7799 vector local_hit_pos;
7802 // get the data for the pain packet
7803 offset = HEADER_LENGTH;
7807 GET_DATA(local_hit_pos);
7810 mprintf(("PAIN!\n"));
7812 // get weapon info pointer
7813 Assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER));
7814 if(! ((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
7817 wip = &Weapon_info[windex];
7819 // play the weapon hit sound
7820 Assert(Player_obj != NULL);
7821 if(Player_obj == NULL){
7824 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
7826 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
7827 ship_hit_pain((float)udamage);
7830 ship_apply_whack(&force, &local_hit_pos, Player_obj);
7834 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
7836 ubyte data[MAX_PACKET_SIZE];
7838 int packet_size = 0;
7840 // build the header and add the data
7841 BUILD_HEADER(LIGHTNING_PACKET);
7842 val = (char)bolt_type;
7845 ADD_DATA((*strike));
7847 // send to everyone unreliable for now
7848 multi_io_send_to_all(data, packet_size);
7851 void process_lightning_packet(ubyte *data, header *hinfo)
7855 vector start, strike;
7858 offset = HEADER_LENGTH;
7859 GET_DATA(bolt_type);
7870 nebl_bolt(bolt_type, &start, &strike);
7873 void send_bytes_recvd_packet(net_player *pl)
7875 // only clients should ever be doing this
7880 ubyte data[MAX_PACKET_SIZE];
7881 int packet_size = 0;
7882 BUILD_HEADER(BYTES_SENT);
7883 ADD_DATA(pl->cl_bytes_recvd);
7885 // send to the server
7886 multi_io_send_reliable(pl, data, packet_size);
7889 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
7893 net_player *pl = NULL;
7894 int offset = HEADER_LENGTH;
7900 if(Net_player == NULL){
7903 if(!MULTIPLAYER_MASTER){
7907 // make sure we know what player sent this
7908 pid = find_player_id(hinfo->id);
7909 if((pid < 0) || (pid >= MAX_PLAYERS)){
7912 pl = &Net_players[pid];
7915 pl->cl_bytes_recvd = bytes;
7919 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
7922 pl->sv_bytes_sent = 0;
7926 void send_host_captain_change_packet(short player_id, int captain_change)
7928 ubyte data[MAX_PACKET_SIZE];
7929 int packet_size = 0;
7932 BUILD_HEADER(TRANSFER_HOST);
7933 ADD_DATA(player_id);
7934 ADD_DATA(captain_change);
7937 multi_io_send_to_all_reliable(data, packet_size);
7940 void process_host_captain_change_packet(ubyte *data, header *hinfo)
7942 int offset = HEADER_LENGTH;
7943 int idx, found_player, captain_change;
7946 // get the player id
7947 GET_DATA(player_id);
7948 GET_DATA(captain_change);
7954 for(idx=0; idx<MAX_PLAYERS; idx++){
7955 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
7956 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
7961 // unflag all old players
7962 for(idx=0; idx<MAX_PLAYERS; idx++){
7963 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
7968 for(idx=0; idx<MAX_PLAYERS; idx++){
7969 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
7970 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
7972 // spew to the HUD config
7973 if(Net_players[idx].player != NULL){
7974 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
7984 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
7989 void send_self_destruct_packet()
7991 ubyte data[MAX_PACKET_SIZE];
7992 int packet_size = 0;
7995 if(Net_player == NULL){
7999 // if i'm the server, I shouldn't be here
8000 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8001 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8005 // only if this is valid
8006 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8011 if((Player_ship == NULL) || (Player_obj == NULL)){
8016 BUILD_HEADER(SELF_DESTRUCT);
8017 ADD_DATA(Player_obj->net_signature);
8019 // send to the server
8020 multi_io_send_reliable(Net_player, data, packet_size);
8023 void process_self_destruct_packet(ubyte *data, header *hinfo)
8025 int offset = HEADER_LENGTH;
8029 // get the net signature
8034 np_index = find_player_id(hinfo->id);
8038 if(MULTI_OBSERVER(Net_players[np_index])){
8041 if(Net_players[np_index].player == NULL){
8044 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8047 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8050 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8053 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8058 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);