2 * Copyright (C) Volition, Inc. 2005. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
11 * $Logfile: /Freespace2/code/Network/multi_fstracker.cpp $
13 * $Date: 9/13/99 11:30a $
16 * $Log: /Freespace2/code/Network/multi_fstracker.cpp $
18 * 19 9/13/99 11:30a Dave
19 * Added checkboxes and functionality for disabling PXO banners as well as
20 * disabling d3d zbuffer biasing.
22 * 18 8/30/99 5:01p Dave
23 * Made d3d do less state changing in the nebula. Use new chat server for
26 * 17 8/25/99 4:38p Dave
27 * Updated PXO stuff. Make squad war report stuff much more nicely.
29 * 16 8/19/99 10:59a Dave
30 * Packet loss detection.
32 * 15 6/07/99 11:30p Dave
35 * 14 4/30/99 12:18p Dave
36 * Several minor bug fixes.
38 * 13 4/09/99 2:21p Dave
39 * Multiplayer beta stuff. CD checking.
41 * 12 2/25/99 4:19p Dave
42 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
43 * release build warnings. Added more data to the squad war request and
46 * 11 2/24/99 2:25p Dave
47 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
48 * bug for dogfight more.
50 * 10 2/17/99 2:11p Dave
51 * First full run of squad war. All freespace and tracker side stuff
54 * 9 2/12/99 6:16p Dave
55 * Pre-mission Squad War code is 95% done.
57 * 8 2/11/99 3:08p Dave
58 * PXO refresh button. Very preliminary squad war support.
60 * 7 2/08/99 5:07p Dave
61 * FS2 chat server support. FS2 specific validated missions.
63 * 6 2/04/99 6:29p Dave
64 * First full working rev of FS2 PXO support. Fixed Glide lighting
67 * 5 2/03/99 6:06p Dave
68 * Groundwork for FS2 PXO usertracker support. Gametracker support next.
70 * 4 12/03/98 5:22p Dave
71 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
74 * 3 10/19/98 11:15a Dave
75 * Changed requirements for stats storing in PXO mode.
77 * 2 10/07/98 10:53a Dave
80 * 1 10/07/98 10:50a Dave
82 * 46 9/18/98 2:22a Dave
83 * Fixed freespace-side PXO api to correctly handle length 10 id strings.
84 * Fixed team select screen to handle alpha/beta/gamma ships which are not
85 * marked as OF_PLAYER_SHIP
87 * 45 9/16/98 6:54p Dave
88 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
89 * the ship list box. Added code so that tracker stats are not stored with
92 * 44 9/15/98 7:24p Dave
93 * Minor UI changes. Localized bunch of new text.
95 * 43 9/11/98 4:14p Dave
96 * Fixed file checksumming of < file_size. Put in more verbose kicking and
97 * PXO stats store reporting.
99 * 42 9/10/98 1:17p Dave
100 * Put in code to flag missions and campaigns as being MD or not in Fred
101 * and Freespace. Put in multiplayer support for filtering out MD
102 * missions. Put in multiplayer popups for warning of non-valid missions.
104 * 41 9/09/98 5:53p Dave
105 * Put in new tracker packets in API. Change cfile to be able to checksum
106 * portions of a file.
108 * 40 9/04/98 3:51p Dave
109 * Put in validated mission updating and application during stats
112 * 39 9/01/98 6:48p Dave
113 * Energy suck weapon. Removed a couple of now-bogus asserts in tracker
116 * 38 8/21/98 1:14p Dave
117 * Put in log system hooks in useful places.
119 * 37 8/07/98 10:39a Allender
120 * fixed debug standalone problem where stats would continually get sent
121 * to tracker. more debug code to help find stats problem
123 * 36 7/24/98 11:14a Allender
124 * preparation for validated missions
126 * 35 6/17/98 10:56a Dave
127 * Put in debug code for detecting potential tracker stats update
130 * 34 6/13/98 9:32p Mike
131 * Kill last character in file which caused "Find in Files" to report the
132 * file as "not a text file."
134 * 33 6/13/98 6:01p Hoffoss
135 * Externalized all new (or forgot to be added) strings to all the code.
137 * 32 5/24/98 11:33a Dave
138 * Simplified the stats store process somewhat. Put in checks to find
139 * invalid situations.
141 * 31 5/24/98 10:36a Dave
142 * Put in more checks/verifications for tracker stats updating.
144 * 30 5/22/98 9:35p Dave
145 * Put in channel based support for PXO. Put in "shutdown" button for
146 * standalone. UI tweaks for TvT
148 * 29 5/21/98 9:45p Dave
149 * Lengthened tracker polling times. Put in initial support for PXO
150 * servers with channel filters. Fixed several small UI bugs.
152 * 28 5/21/98 1:52a Dave
153 * Remove obsolete command line functions. Reduce shield explosion packets
154 * drastically. Tweak PXO screen even more. Fix file xfer system so that
155 * we can guarantee file uniqueness.
157 * 27 5/20/98 2:24a Dave
158 * Fixed server side voice muting. Tweaked multi debrief/endgame
159 * sequencing a bit. Much friendlier for stats tossing/accepting now.
161 * 26 5/18/98 9:15p Dave
162 * Put in network config file support.
164 * 25 5/18/98 10:39a Dave
165 * Put in support for new tracker stats.
167 * 24 5/14/98 12:40a Dave
168 * Still more additions to the PXO screen. Updated tracker code.
170 * 23 5/13/98 6:54p Dave
171 * More sophistication to PXO interface. Changed respawn checking so
172 * there's no window for desynchronization between the server and the
175 * 22 5/08/98 7:08p Dave
176 * Lots of UI tweaking.
178 * 21 5/07/98 6:26p Dave
179 * Fix strange boundary conditions which arise when players die/respawn
180 * while the game is being ended. Spiff up the chatbox doskey thing a bit.
182 * 20 5/05/98 3:12p Chad
183 * Process _all_ server entries in a tracker game_list struct.
185 * 19 5/05/98 2:10p Dave
186 * Verify campaign support for testing. More new tracker code.
188 * 18 5/04/98 10:39p Dave
189 * Put in endgame sequencing. Need to check campaign situations.
190 * Realigned ship info on team select screen.
192 * 17 5/04/98 1:43p Dave
193 * Fixed up a standalone resetting problem. Fixed multiplayer stats
194 * collection for clients. Make sure all multiplayer ui screens have the
195 * correct palette at all times.
197 * 16 5/02/98 5:38p Dave
198 * Put in new tracker API code. Put in ship information on mp team select
199 * screen. Make standalone server name permanent. Fixed standalone server
202 * 15 4/30/98 12:57a Dave
203 * Put in new mode for ship/weapon selection. Rearranged how game querying
206 * 14 4/29/98 12:11a Dave
207 * Put in first rev of full API support for new master tracker.
209 * 13 4/28/98 7:50p Dave
210 * Fixing a broken makefile.
212 * 12 4/28/98 5:10p Dave
213 * Fixed multi_quit_game() client side sequencing problem. Turn off
214 * afterburners when ending multiplayer mission. Begin integration of mt
215 * API from Kevin Bentley.
217 * 11 4/04/98 4:22p Dave
218 * First rev of UDP reliable sockets is done. Seems to work well if not
221 * 10 3/15/98 4:17p Dave
222 * Fixed oberver hud problems. Put in handy netplayer macros. Reduced size
223 * of network orientation matrices.
225 * 9 2/10/98 8:39p Dave
226 * Fixed bugs. Made endgame sequencing more clear.
228 * 8 2/07/98 1:51p Dave
229 * Centralized single and multiplayer stats tallying.
231 * 7 2/05/98 7:13p Dave
232 * Added some more security to MT communications.
234 * 6 2/04/98 6:35p Dave
235 * Changed psnet to use raw data with no headers. Started putting in
236 * support for master tracker security measures.
238 * 5 2/03/98 8:18p Dave
239 * More MT stats transfer stuff.
241 * 4 2/02/98 8:44p Dave
242 * Finished redoing master tracker stats transfer.
244 * 3 1/31/98 4:32p Dave
245 * Put in new support for VMT player validation, game logging in, and game
246 * logging out. Need to finish stats transfer.
248 * 2 1/30/98 5:53p Dave
249 * Revamped master tracker API
251 * 1 1/30/98 5:50p Dave
258 #include <netinet/in.h>
261 #include "freespace.h"
263 #include "gamesequence.h"
266 #include "valid.h" // tracker API
267 #include "gtrack.h" // tracker API
268 #include "ptrack.h" // tracker API
270 #include "multi_fstracker.h"
271 #include "multiutil.h"
273 #include "multimsgs.h"
274 #include "multi_log.h"
275 #include "stand_server.h"
276 #include "multi_pmsg.h"
278 // -----------------------------------------------------------------------------------
279 // FREESPACE MASTER TRACKER DEFINES/VARS
282 // if the fs tracker module has been successfully initialized
283 int Multi_fs_tracker_inited = 0;
285 // if we're currently performing some operation with the tracker
286 int Multi_fs_tracker_busy = 0;
288 // channel to associate when creating a server
289 char Multi_fs_tracker_channel[255] = "";
291 // channel to use when polling the tracker for games
292 char Multi_fs_tracker_filter[255] = "";
295 // -----------------------------------------------------------------------------------
296 // FREESPACE MASTER TRACKER FORWARD DECLARATIONS
299 // used with popup_till_condition() for validating freespace pilots
300 #define MT_VALIDATE_NOT_DONE 0 // still in the process of validation
301 #define MT_VALIDATE_SUCCEED 1 // successfully validated the pilot
302 #define MT_VALIDATE_FAIL 2 // failed in validating the pilot
303 #define MT_VALIDATE_TIMEOUT 3 // timedout on contacting the tracker
304 #define MT_VALIDATE_CANCEL 4 // if the action was cancelled
305 #define MT_PILOT_VAL_TIMEOUT 5000 // timeout for validating a pilot
306 int Multi_validate_mode; // 0 == getting player id, 1 == getting player stats
307 int multi_fs_validate_process();
309 // used with popup_till_condition() for logging in freespace games
310 #define MT_LOGIN_NOT_DONE 0 // still in the process of logging in
311 #define MT_LOGIN_SUCCEED 1 // successfully logged the game in
312 #define MT_LOGIN_TIMEOUT 2 // timedout on contacting the tracker
314 // used with popup_till_condition() for storing player stats at the end of a freespace game
315 #define MT_STATS_NOT_DONE 0 // still in the process of storing stats
316 #define MT_STATS_SUCCEED 1 // successfully logged all player stats
318 // used with popup_till_condition() for validating missions
319 #define MT_MVALID_NOT_DONE 0 // still in the process of validating
320 #define MT_MVALID_VALID 1 // mission is valid
321 #define MT_MVALID_INVALID 2 // mission is invalid
322 #define MT_MVALID_ERROR 3 // error while performing operation. assume invalid
324 // store stats mode defined
325 #define MT_STORE_STATS_VALIDATE 0
326 #define MT_STORE_STATS_GET_STATS 1
327 #define MT_STORE_STATS_ACCEPT 2
328 #define MT_STORE_STATS_SEND_STATS 3
330 int Multi_store_stats_mode; // 0 == initial request for player stats, 1 == waiting for player stats, 2 == tallying stats locally, 3 == sending stats to tracker
331 int Multi_store_stats_player_index; // player we're currently working with
332 int Multi_store_stats_player_flag; // if we're finished with the current guy
333 vmt_stats_struct Multi_store_stats_stats; //
334 int multi_fs_store_stats_do(); // manage all master tracker stats storing
335 int multi_fs_store_stats_get_next_player(int cur_player);
337 int Multi_tracker_player_is_valid = 0;
338 int Multi_tracker_got_response = 0;
340 // copy a freespace stats struct to a tracker-freespace stats struct
341 void multi_stats_fs_to_tracker(scoring_struct *fs, vmt_stats_struct *vmt, player *pl, int tracker_id);
343 // copy a tracker-freespace stats struct to a freespace stats struct
344 void multi_stats_tracker_to_fs(vmt_stats_struct *vmt, scoring_struct *fs);
346 // process an incoming active game item
347 void multi_fs_tracker_process_game_item(game_list *gl);
349 // verify that there are no duplicate tracker id's to this one
350 void multi_fs_tracker_check_dup(int tracker_id,int player_index);
352 // verify that there are no duplicate pilot callsigns
353 void multi_fs_tracker_check_dup_callsign(net_player *player,int player_index);
355 // report on the results of the stats store procedure
356 void multi_fs_tracker_report_stats_results();
358 // tracker specific data structures
359 pxo_net_game_data Multi_tracker_game_data;
360 vmt_stats_struct Multi_tracker_fs_pilot;
361 squad_war_response Multi_tracker_sw_response;
363 // -----------------------------------------------------------------------------------
364 // FREESPACE MASTER TRACKER DEFINITIONS
367 // give some processor time to the tracker API
368 void multi_fs_tracker_process()
373 PSNET_TOP_LAYER_PROCESS();
376 if(Multi_fs_tracker_inited){
377 // pilot validation system
380 // pilot tracing system
383 // game tracking system
386 // set if we've got any pending game list items
389 multi_fs_tracker_process_game_item(gl);
395 // initialize the master tracker API for Freespace
396 void multi_fs_tracker_init()
398 // don't do anything if we're already initialized
399 if(Multi_fs_tracker_inited){
403 // initialize the low-level validation stuff
404 if(!InitValidateClient()){
405 ml_printf("Error initializing tracker api (validateclient)\n");
409 // initialize the low-level pilot tracking stuff
410 if(!InitPilotTrackerClient()){
411 ml_printf("Error initializing tracker api (pilotclient)\n");
415 // intialize the low-level game tracking stuff
417 if(!InitGameTrackerClient(GT_FREESPACE2)){
419 if(!InitGameTrackerClient(GT_FREESPACE)){
421 ml_printf("Error initializing tracker api (gameclient)\n");
425 nprintf(("Network","Successfully initialized tracker api\n"));
427 // we've successfully initialized the tracker stuff
428 Multi_fs_tracker_inited = 1;
431 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
432 int multi_fs_tracker_validate(int show_error)
434 validate_id_request vir;
436 if(!Multi_fs_tracker_inited){
437 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Warning, Parallax Online startup failed. Will not be able to play tracker games!",666));
441 // set this to false for now
442 Multi_tracker_player_is_valid = 0;
443 Multi_tracker_got_response = 0;
445 // mark the module as busy
446 Multi_fs_tracker_busy = 1;
449 // validate our pilot on the master tracker if possible
450 memset(&vir,0,sizeof(vir));
451 SDL_zero(Multi_tracker_id_string);
452 SDL_strlcpy(vir.login, Multi_tracker_login, SDL_arraysize(vir.login));
453 SDL_strlcpy(vir.password, Multi_tracker_passwd, SDL_arraysize(vir.password));
454 ValidateUser(&vir,Multi_tracker_id_string);
456 // set validation mode
457 Multi_validate_mode = 0;
459 int rval = popup_till_condition(multi_fs_validate_process,XSTR("&Cancel",667),XSTR("Attempting to validate pilot ...",668));
461 // if we failed for one reason or another
462 case MT_VALIDATE_FAIL :
463 // if we're supposed to show error codes
465 popup(PF_USE_AFFIRMATIVE_ICON | PF_BODY_BIG,1,XSTR("&Ok",669),XSTR("Pilot rejected by Parallax Online!",670));
468 Multi_validate_mode = -1;
470 Multi_fs_tracker_busy = 0;
473 case MT_VALIDATE_SUCCEED :
475 if(Multi_tracker_fs_pilot.virgin_pilot){
476 multi_common_add_notify(XSTR("Successfully created and validated new pilot!",671));
478 multi_common_add_notify(XSTR("Parallax Online pilot validation succeeded!",672));
481 // copy my statistics into my pilot file
482 multi_stats_tracker_to_fs(&Multi_tracker_fs_pilot,&Player->stats);
484 Multi_validate_mode = -1;
486 Multi_fs_tracker_busy = 0;
489 case MT_VALIDATE_TIMEOUT :
490 rval = popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_BODY_BIG,2,XSTR("&Abort",673),XSTR("&Retry",674),XSTR("Validation timed out",675));
492 // if the user clicked abort, then leave. otherwise try again
494 Multi_validate_mode = -1;
496 Multi_fs_tracker_busy = 0;
502 Multi_validate_mode = -1;
504 Multi_fs_tracker_busy = 0;
506 // essentially, cancel
512 // attempt to log the current game server in with the master tracker
513 void multi_fs_tracker_login_freespace()
515 if(!Multi_fs_tracker_inited){
516 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Warning, Parallax Online startup failed. Will not be able to play tracker games!",666));
520 // if we're already logged into a game, don't do anything
521 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) || (Net_player->flags & NETINFO_FLAG_MT_CONNECTED) ) {
525 // pretty much all we do is make 1 call
526 memset(&Multi_tracker_game_data, 0, sizeof(Multi_tracker_game_data));
527 SDL_strlcpy(Multi_tracker_game_data.game_name, Netgame.name, SDL_arraysize(Multi_tracker_game_data.game_name));
528 Multi_tracker_game_data.difficulty = 99;
529 Multi_tracker_game_data.type = 0;
530 Multi_tracker_game_data.state = 1;
531 Multi_tracker_game_data.max_players = 12;
532 Multi_tracker_game_data.current_num_players = 0;
534 // if we have a valid channel string, use it
535 if(strlen(Multi_fs_tracker_channel)){
536 SDL_strlcpy(Multi_tracker_game_data.channel, Multi_fs_tracker_channel, SDL_arraysize(Multi_tracker_game_data.channel));
539 StartTrackerGame(&Multi_tracker_game_data);
540 Net_player->flags |= NETINFO_FLAG_MT_CONNECTED;
543 ml_string(NOX("Server connected to Game Tracker"));
546 // attempt to update all player statistics and scores on the tracker
547 int multi_fs_tracker_store_stats()
552 ml_string(NOX("Server storing stats on User Tracker"));
554 // retrieve stats from tracker
555 Multi_store_stats_mode = MT_STORE_STATS_VALIDATE;
557 // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
558 Multi_store_stats_player_index = -1;
559 Multi_store_stats_player_flag = 1;
561 // mark the module as busy
562 Multi_fs_tracker_busy = 1;
564 // unmark everyone's GET_FAILED flag
565 for(idx=0;idx<MAX_PLAYERS;idx++){
566 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_GET_FAILED);
567 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_SEND_FAILED);
568 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_DONE);
572 // if playing with an invalid ships.tbl
573 if(!Game_ships_tbl_valid){
574 send_game_chat_packet(Net_player, XSTR("<Server detected a hacked ships.tbl. Stats will not be saved>", 1044), MULTI_MSG_ALL, NULL, NULL, 1);
575 multi_display_chat_msg(XSTR("<Server detected a hacked ships.tbl. Stats will not be saved>", 1044), 0, 0);
576 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You are playing with a hacked ships.tbl, your stats will not be saved", 1045) );
577 multi_fs_tracker_report_stats_results();
578 Multi_fs_tracker_busy = 0;
582 // if playing with an invalid weapons.tbl
583 if(!Game_weapons_tbl_valid){
584 send_game_chat_packet(Net_player, XSTR("<Server detected a hacked weapons.tbl. Stats will not be saved>", 1046), MULTI_MSG_ALL, NULL, NULL, 1);
585 multi_display_chat_msg(XSTR("<Server detected a hacked weapons.tbl. Stats will not be saved>", 1046), 0, 0);
586 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You are playing with a hacked weapons.tbl, your stats will not be saved", 1047) );
587 multi_fs_tracker_report_stats_results();
588 Multi_fs_tracker_busy = 0;
592 // if there is only 1 player, don't store the stats
593 if((multi_num_players() <= 1) && (Multi_num_players_at_start <= 1)){
594 send_game_chat_packet(Net_player, XSTR("<Not enough players were present at game start or end, stats will not be saved>", 1048), MULTI_MSG_ALL, NULL, NULL, 1);
595 multi_display_chat_msg(XSTR("<Not enough players were present at game start or end, stats will not be saved>", 1048), 0, 0);
596 multi_fs_tracker_report_stats_results();
597 Multi_fs_tracker_busy = 0;
601 // if any players have hacked info
602 for(idx=0; idx<MAX_PLAYERS; idx++){
603 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
608 // check to see if the mission is valid
609 if(multi_fs_tracker_validate_mission(Game_current_mission_filename) != MVALID_STATUS_VALID){
610 send_game_chat_packet(Net_player, XSTR("<Server detected a non PXO validated mission. Stats will not be saved>", 1049), MULTI_MSG_ALL, NULL, NULL, 1);
611 multi_display_chat_msg(XSTR("<Server detected a non PXO validated mission. Stats will not be saved>", 1049), 0, 0);
612 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK, XSTR("This is not a PXO validated mission, your stats will not be saved", 1050));
613 Multi_fs_tracker_busy = 0;
618 popup_till_condition(multi_fs_store_stats_do,XSTR("&Cancel",667), XSTR("Sending player stats requests ...",676));
620 // send appropriate chat messages indicating stats store failure
621 multi_fs_tracker_report_stats_results();
623 // mark the module as not busy anymore
624 Multi_fs_tracker_busy = 0;
629 // attempt to update all player statistics (standalone mode)
630 int multi_fs_std_tracker_store_stats()
635 // don't do anything if this is a tracker game
636 if(!(MULTI_IS_TRACKER_GAME)){
640 if(!Multi_fs_tracker_inited){
645 ml_string(NOX("Standalone server storing stats on User Tracker"));
647 // retrieve stats from tracker
648 Multi_store_stats_mode = MT_STORE_STATS_VALIDATE;
650 // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
651 Multi_store_stats_player_index = -1;
652 Multi_store_stats_player_flag = 1;
654 // mark the module as busy
655 Multi_fs_tracker_busy = 1;
657 // unmark everyone's GET_FAILED flag
658 for(idx=0;idx<MAX_PLAYERS;idx++){
659 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_GET_FAILED);
660 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_SEND_FAILED);
661 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_DONE);
665 // if playing with an invalid ships.tbl
666 if(!Game_ships_tbl_valid){
667 send_game_chat_packet(Net_player, XSTR("<Server detected a hacked ships.tbl. Stats will not be saved>", 1044), MULTI_MSG_ALL, NULL, NULL, 1);
668 multi_display_chat_msg(XSTR("<Server detected a hacked ships.tbl. Stats will not be saved>", 1044), 0, 0);
669 multi_fs_tracker_report_stats_results();
670 Multi_fs_tracker_busy = 0;
674 // if playing with an invalid weapons.tbl
675 if(!Game_weapons_tbl_valid){
676 send_game_chat_packet(Net_player, XSTR("<Server detected a hacked weapons.tbl. Stats will not be saved>", 1046), MULTI_MSG_ALL, NULL, NULL, 1);
677 multi_display_chat_msg(XSTR("<Server detected a hacked weapons.tbl. Stats will not be saved>", 1046), 0, 0);
678 multi_fs_tracker_report_stats_results();
679 Multi_fs_tracker_busy = 0;
683 // if any players have hacked info
684 for(idx=0; idx<MAX_PLAYERS; idx++){
685 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
690 // if there is only 1 player, don't store the stats
691 if((multi_num_players() <= 1) && (Multi_num_players_at_start <= 1)){
692 send_game_chat_packet(Net_player, XSTR("<Not enough players were present at game start or end, stats will not be saved>", 1048), MULTI_MSG_ALL, NULL, NULL, 1);
693 multi_display_chat_msg(XSTR("<Not enough players were present at game start or end, stats will not be saved>", 1048), 0, 0);
694 multi_fs_tracker_report_stats_results();
695 Multi_fs_tracker_busy = 0;
699 // check to see if the mission is valid
700 if(multi_fs_tracker_validate_mission(Game_current_mission_filename) != MVALID_STATUS_VALID){
701 send_game_chat_packet(Net_player, XSTR("<Server detected a non PXO validated mission. Stats will not be saved>", 1049), MULTI_MSG_ALL, NULL, NULL, 1);
702 multi_display_chat_msg(XSTR("<Server detected a non PXO validated mission. Stats will not be saved>", 1049), 0, 0);
703 Multi_fs_tracker_busy = 0;
708 // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
710 ret_val = multi_fs_store_stats_do();
711 game_set_frametime(GS_STATE_STANDALONE_POSTGAME);
713 } while(ret_val == MT_STATS_NOT_DONE);
715 // report on the results
716 multi_fs_tracker_report_stats_results();
718 // mark the module as no longer busy
719 Multi_fs_tracker_busy = 0;
724 // log freespace out of the tracker
725 void multi_fs_tracker_logout()
727 if(!Multi_fs_tracker_inited){
731 // make sure we're connected
732 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER) || !(Net_player->flags & NETINFO_FLAG_MT_CONNECTED)){
736 // otherwise, log us out
740 memset(&Multi_tracker_game_data, 0, sizeof(Multi_tracker_game_data));
741 Net_player->flags &= ~(NETINFO_FLAG_MT_CONNECTED);
744 ml_string(NOX("Server disconnecting from Game Tracker"));
747 // send a request for a list of games
748 void multi_fs_tracker_send_game_request()
750 filter_game_list_struct filter;
753 // if we're not initialized, don't do anything
754 if(!Multi_fs_tracker_inited){
758 // if we have a valid filter, use that instead
759 len = strlen(Multi_fs_tracker_filter);
760 if((len > 0) && (len < CHANNEL_LEN-1) ){
761 memset(&filter,0,sizeof(filter_game_list_struct));
763 SDL_strlcpy(filter.channel, Multi_fs_tracker_filter, SDL_arraysize(filter.channel));
764 RequestGameListWithFilter(&filter);
771 // if the API has successfully been initialized and is running
772 int multi_fs_tracker_inited()
774 return Multi_fs_tracker_inited;
777 // update our settings on the tracker regarding the current netgame stuff
778 void multi_fs_tracker_update_game(netgame_info *ng)
780 if(!Multi_fs_tracker_inited){
784 // copy in the relevant data
785 Multi_tracker_game_data.max_players = ng->max_players;
786 Multi_tracker_game_data.current_num_players = multi_num_players();
789 memset(Multi_tracker_game_data.players, 0 ,MAX_FREESPACE_PLAYERS * MAX_FREESPACE_PLAYER_NAME_LEN);
791 for(idx=0;idx<MAX_PLAYERS;idx++){
792 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
793 strcpy(Multi_tracker_game_data.players[count], Net_players[idx].player->callsign);
794 Multi_tracker_game_data.player_rank[count] = Net_players[idx].player->stats.rank;
800 SDL_strlcpy(Multi_tracker_game_data.mission_name, ng->name, SDL_arraysize(Multi_tracker_game_data.mission_name));
803 ml_string(NOX("Server updating netgame info for Game Tracker"));
806 // if we're currently busy performing some tracker operation (ie, you should wait or not)
807 int multi_fs_tracker_busy()
809 return Multi_fs_tracker_busy;
813 // -----------------------------------------------------------------------------------
814 // FREESPACE MASTER TRACKER FORWARD DEFINITIONS
817 // used with popup_till_condition() for validating freespace pilots
818 int multi_fs_validate_process()
820 // should never be here if this is not true
821 SDL_assert(Multi_fs_tracker_inited);
824 PSNET_TOP_LAYER_PROCESS();
827 // if we're still in player validation mode
828 if(Multi_validate_mode == 0){
829 switch(ValidateUser(NULL,NULL)){
830 // timeout on waiting for response
832 return MT_VALIDATE_TIMEOUT;
836 // set tracker id to -1
837 SDL_strlcpy(Multi_tracker_id_string, "-1", SDL_arraysize(Multi_tracker_id_string));
838 Multi_tracker_id = -1;
839 return MT_VALIDATE_FAIL;
843 return MT_VALIDATE_NOT_DONE;
847 // now we need to try and receive stats
849 // mark me as being valid
850 Multi_tracker_player_is_valid = 1;
852 // change the popup text
853 popup_change_text(XSTR("Attempting to get pilot stats ...",679));
855 // get my tracker id#
856 Multi_tracker_id = atoi(Multi_tracker_id_string);
857 SDL_assert(Multi_tracker_id != -1);
859 GetFSPilotData((vmt_stats_struct*)0xffffffff,NULL,NULL,0);
860 GetFSPilotData(&Multi_tracker_fs_pilot,Player->callsign,Multi_tracker_id_string,1);
863 Multi_validate_mode = 1;
864 return MT_VALIDATE_NOT_DONE;
870 switch(GetFSPilotData(NULL,NULL,NULL,0)){
873 return MT_VALIDATE_TIMEOUT;
877 return MT_VALIDATE_NOT_DONE;
881 return MT_VALIDATE_SUCCEED;
885 return MT_VALIDATE_FAIL;
889 // we're not done yet - probably should never get here
890 return MT_VALIDATE_NOT_DONE;
893 // used with popup_till_condition() for storing player stats at the end of a freespace game
894 int multi_fs_store_stats_do()
896 char tracker_id_string[512];
897 char popup_text[100];
899 SDL_assert(Multi_fs_tracker_inited);
902 PSNET_TOP_LAYER_PROCESS();
905 switch(Multi_store_stats_mode){
906 // get stats for all players
907 case MT_STORE_STATS_VALIDATE :
908 Multi_store_stats_mode = MT_STORE_STATS_GET_STATS;
911 case MT_STORE_STATS_GET_STATS:
912 // if we need to get the next player
913 if(Multi_store_stats_player_flag){
914 Multi_store_stats_player_index = multi_fs_store_stats_get_next_player(Multi_store_stats_player_index);
916 // if it returns < 0 we're done with all players and should move onto the next stage (applying mission stats)
917 if(Multi_store_stats_player_index < 0){
918 Multi_store_stats_mode = MT_STORE_STATS_ACCEPT;
919 return MT_STATS_NOT_DONE;
922 // unset this flag so we process the request
923 Multi_store_stats_player_flag = 0;
925 // fill out the information request
926 memset(tracker_id_string,0,512);
927 SDL_assert(Net_players[Multi_store_stats_player_index].tracker_player_id > 0);
929 // verify that there are no duplicate tracker id's to this one
930 multi_fs_tracker_check_dup(Net_players[Multi_store_stats_player_index].tracker_player_id,Multi_store_stats_player_index);
931 multi_fs_tracker_check_dup_callsign(&Net_players[Multi_store_stats_player_index],Multi_store_stats_player_index);
933 SDL_snprintf(tracker_id_string, SDL_arraysize(tracker_id_string), "%d", Net_players[Multi_store_stats_player_index].tracker_player_id);
934 Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = -1;
935 Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = 0;
937 // send the request itself
938 GetFSPilotData((vmt_stats_struct*)0xffffffff, NULL, NULL,0);
939 memset(&Multi_store_stats_stats, 0, sizeof(Multi_store_stats_stats));
940 if(GetFSPilotData(&Multi_store_stats_stats, Net_players[Multi_store_stats_player_index].player->callsign,tracker_id_string,1) != 0){
943 // move onto the next player
944 Multi_store_stats_player_flag = 1;
945 return MT_STATS_NOT_DONE;
948 // set the popup text
949 if(!(Game_mode & GM_STANDALONE_SERVER)){
950 SDL_snprintf(popup_text, SDL_arraysize(popup_text), XSTR("Getting player stats for %s...\n", 680), Net_players[Multi_store_stats_player_index].player->callsign);
951 popup_change_text(popup_text);
953 return MT_STATS_NOT_DONE;
956 // process the request
957 switch(GetFSPilotData(NULL,NULL,NULL,0)){
960 // copy his stats, then flag him as done so we move onto the next guys
961 multi_stats_tracker_to_fs(&Multi_store_stats_stats,&Net_players[Multi_store_stats_player_index].player->stats);
963 // make sure we apply his mission stats now
964 scoring_do_accept(&Net_players[Multi_store_stats_player_index].player->stats);
968 // debug code to check for bogus stats
969 scoring_struct *ssp = &(Net_players[Multi_store_stats_player_index].player->stats);
970 vmt_stats_struct *vmt = &Multi_store_stats_stats;
972 if ( (ssp->missions_flown < vmt->missions_flown) || (ssp->flight_time < ssp->flight_time) || (ssp->kill_count < vmt->kill_count) ) {
978 // flag him as being completed
979 Multi_store_stats_player_flag = 1;
981 // also store this last security value so we can properly update him
982 Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = Multi_store_stats_stats.security;
983 Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = Multi_store_stats_stats.checksum;
991 case 3: case -2: case 2: case -3: case -1:
992 // this shouldn't be happening under most conditions. For debugging....
995 // flag him as done so we move onto the next guy
996 Multi_store_stats_player_flag = 1;
997 Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = -1;
998 Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = 0;
1000 // mark down that the stats get for him failed
1001 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_GET_FAILED;
1002 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1007 // update all stats for all players locally and on client machines
1008 case MT_STORE_STATS_ACCEPT :
1009 // tell everyone to save their stats
1010 send_store_stats_packet(1);
1012 // reset status flags and indices
1013 Multi_store_stats_player_index = -1;
1014 Multi_store_stats_player_flag = 1;
1016 Multi_store_stats_mode = MT_STORE_STATS_SEND_STATS;
1019 // send stats to the tracker
1020 case MT_STORE_STATS_SEND_STATS:
1021 // if we need to get the next player
1022 if(Multi_store_stats_player_flag){
1023 Multi_store_stats_player_index = multi_fs_store_stats_get_next_player(Multi_store_stats_player_index);
1025 // if it returns < 0 we need to move onto the next player
1026 if(Multi_store_stats_player_index < 0){
1027 return MT_STATS_SUCCEED;
1030 Multi_store_stats_player_flag = 0;
1032 // fill in the information
1033 memset(&Multi_store_stats_stats,0,sizeof(Multi_store_stats_stats));
1035 SDL_assert(Net_players[Multi_store_stats_player_index].tracker_player_id > 0);
1037 // verify that there are no duplicate tracker id's to this one
1038 multi_fs_tracker_check_dup(Net_players[Multi_store_stats_player_index].tracker_player_id,Multi_store_stats_player_index);
1039 multi_fs_tracker_check_dup_callsign(&Net_players[Multi_store_stats_player_index],Multi_store_stats_player_index);
1040 multi_stats_fs_to_tracker(&Net_players[Multi_store_stats_player_index].player->stats,&Multi_store_stats_stats,Net_players[Multi_store_stats_player_index].player,Net_players[Multi_store_stats_player_index].tracker_player_id);
1042 Multi_store_stats_stats.security = Net_players[Multi_store_stats_player_index].s_info.tracker_security_last;
1044 // SDL_assert(Net_players[Multi_store_stats_player_index].s_info.tracker_checksum != 0);
1045 Multi_store_stats_stats.checksum = Net_players[Multi_store_stats_player_index].s_info.tracker_checksum;
1048 SendFSPilotData((vmt_stats_struct*)0xffffffff);
1049 if(SendFSPilotData(&Multi_store_stats_stats) != 0){
1052 // failed to send, try another player the next time around
1053 Multi_store_stats_player_flag = 1;
1054 return MT_STATS_NOT_DONE;
1057 // set the popup text
1058 if(!(Game_mode & GM_STANDALONE_SERVER)){
1059 SDL_snprintf(popup_text, SDL_arraysize(popup_text), XSTR("Updating player stats for %s...\n", 681), Net_players[Multi_store_stats_player_index].player->callsign);
1060 popup_change_text(popup_text);
1063 return MT_STATS_NOT_DONE;
1066 // otherwise check on his status
1067 switch(SendFSPilotData(NULL)){
1069 case -1: case -2: case -3: case 2: case 3:
1070 // flag him as done so we move onto the next guy
1071 Multi_store_stats_player_flag = 1;
1073 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_SEND_FAILED;
1074 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1079 // flag him as done so we move onto the next guys
1080 Multi_store_stats_player_flag = 1;
1081 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1088 // return not done yet
1089 return MT_STATS_NOT_DONE;
1092 // copy a freespace stats struct to a tracker-freespace stats struct
1093 void multi_stats_fs_to_tracker(scoring_struct *fs, vmt_stats_struct *vmt, player *pl, int tracker_id)
1095 char tracker_id_string[256];
1097 // tracker id string
1098 SDL_snprintf(tracker_id_string, SDL_arraysize(tracker_id_string),"%d", tracker_id);
1099 SDL_strlcpy(vmt->tracker_id, tracker_id_string, SDL_arraysize(vmt->tracker_id));
1102 SDL_strlcpy(vmt->pilot_name, pl->callsign, SDL_arraysize(vmt->pilot_name));
1104 // score, rank and medals
1105 vmt->score = fs->score;
1106 vmt->rank = fs->rank;
1108 SDL_assert(MAX_FS2_MEDALS == NUM_MEDALS);
1109 memcpy(vmt->medals, fs->medals, sizeof(int) * MAX_FS2_MEDALS);
1111 vmt->num_medals = MAX_FS2_MEDALS;
1114 memcpy(vmt->medals, fs->medals, sizeof(int) * MAX_FS_MEDALS);
1117 // kills and assists
1119 SDL_assert(MAX_FS2_SHIP_TYPES == MAX_SHIP_TYPES);
1120 memcpy(vmt->kills, fs->kills, sizeof(ushort) * MAX_FS2_SHIP_TYPES);
1122 vmt->num_ship_types = MAX_FS2_SHIP_TYPES;
1125 memcpy(vmt->kills, fs->kills, sizeof(int) * MAX_FS_SHIP_TYPES);
1127 vmt->assists = fs->assists;
1128 vmt->kill_count = fs->kill_count;
1129 vmt->kill_count_ok = fs->kill_count_ok;
1133 vmt->p_shots_fired = fs->p_shots_fired;
1134 vmt->s_shots_fired = fs->s_shots_fired;
1135 vmt->p_shots_hit = fs->p_shots_hit;
1136 vmt->s_shots_hit = fs->s_shots_hit;
1137 vmt->p_bonehead_hits = fs->p_bonehead_hits;
1138 vmt->s_bonehead_hits = fs->s_bonehead_hits;
1139 vmt->bonehead_kills = fs->bonehead_kills;
1141 // missions flown information
1142 vmt->missions_flown = fs->missions_flown;
1143 vmt->flight_time = fs->flight_time;
1144 vmt->last_flown = (unsigned int)fs->last_flown;
1147 // copy a tracker-freespace stats struct to a freespace stats struct
1148 void multi_stats_tracker_to_fs(vmt_stats_struct *vmt,scoring_struct *fs)
1150 #if !defined(MAKE_FS1) && !defined(FS2_DEMO)
1151 int num_medals, num_ship_types;
1154 // score, rank and medals
1155 fs->score = vmt->score;
1156 fs->rank = vmt->rank;
1157 #if !defined(MAKE_FS1) && !defined(FS2_DEMO)
1158 num_medals = vmt->num_medals;
1159 if(num_medals > NUM_MEDALS){
1161 num_medals = NUM_MEDALS;
1163 memset(fs->medals, 0, sizeof(int) * NUM_MEDALS);
1164 memcpy(fs->medals, vmt->medals, sizeof(int) * num_medals);
1166 SDL_zero(fs->medals);
1167 memcpy(fs->medals, vmt->medals, sizeof(int) * NUM_MEDALS);
1170 // kills and assists
1171 #if !defined(MAKE_FS1) && !defined(FS2_DEMO)
1172 num_ship_types = vmt->num_ship_types;
1173 if(num_ship_types > MAX_SHIP_TYPES){
1175 num_ship_types = MAX_SHIP_TYPES;
1177 memset(fs->kills, 0, sizeof(ushort) * MAX_SHIP_TYPES);
1178 memcpy(fs->kills, vmt->kills, sizeof(ushort) * num_ship_types);
1180 SDL_zero(fs->kills);
1181 memcpy(fs->kills, vmt->kills, sizeof(int) * MAX_SHIP_TYPES);
1183 fs->assists = vmt->assists;
1184 fs->kill_count = vmt->kill_count;
1185 fs->kill_count_ok = vmt->kill_count_ok;
1188 fs->p_shots_fired = vmt->p_shots_fired;
1189 fs->s_shots_fired = vmt->s_shots_fired;
1190 fs->p_shots_hit = vmt->p_shots_hit;
1191 fs->s_shots_hit = vmt->s_shots_hit;
1192 fs->p_bonehead_hits = vmt->p_bonehead_hits;
1193 fs->s_bonehead_hits = vmt->s_bonehead_hits;
1194 fs->bonehead_kills = vmt->bonehead_kills;
1196 // missions flown information
1197 fs->missions_flown = vmt->missions_flown;
1198 fs->flight_time = vmt->flight_time;
1199 fs->last_flown = (fs_time_t)vmt->last_flown;
1200 if(fs->last_flown < 0){
1205 // process an incoming active game item
1206 void multi_fs_tracker_process_game_item(game_list *gl)
1211 for(idx=0;idx<MAX_GAME_LISTS_PER_PACKET;idx++){
1212 // skip null server addresses
1213 if(gl->game_server[idx] == 0){
1217 // package up the game information
1218 memset(&ag,0,sizeof(active_game));
1219 SDL_strlcpy(ag.name, gl->game_name[idx], SDL_arraysize(ag.name));
1220 memcpy(&ag.server_addr.addr[0], &gl->game_server[idx], IP_ADDRESS_LENGTH);
1221 ag.server_addr.type = NET_TCP;
1222 ag.server_addr.port = DEFAULT_GAME_PORT;
1224 // add to the active game list
1225 // multi_update_active_games(&ag);
1227 // query this server
1228 send_server_query(&ag.server_addr);
1232 int multi_fs_store_stats_get_next_player(int cur_player)
1236 // if we're at the end of the list
1237 if(cur_player == (MAX_PLAYERS - 1)){
1241 // find the next player
1242 for(idx=cur_player+1;idx<MAX_PLAYERS;idx++){
1244 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].tracker_player_id != -1) && !(Net_players[idx].flags & NETINFO_FLAG_MT_GET_FAILED) ){
1249 // couldn't find one
1253 // verify that there are no duplicate tracker id's to this one
1254 void multi_fs_tracker_check_dup(int tracker_id,int player_index)
1258 // compare against all players except the passed player_index
1259 for(idx=0;idx<MAX_PLAYERS;idx++){
1260 if(idx == player_index){
1264 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].tracker_player_id != -1)){
1265 SDL_assert(Net_players[idx].tracker_player_id != tracker_id);
1270 // verify that there are no duplicate pilot callsigns
1271 void multi_fs_tracker_check_dup_callsign(net_player *player,int player_index)
1275 // compare against all players except the passed player_index
1276 for(idx=0;idx<MAX_PLAYERS;idx++){
1277 if(idx == player_index){
1281 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].tracker_player_id != -1)){
1282 SDL_assert(strcmp(player->player->callsign,Net_players[idx].player->callsign));
1287 // return an MVALID_STATUS_* constant
1288 int multi_fs_tracker_validate_mission_std()
1292 // wait for a response from the tracker
1294 ret_val = ValidateMission(NULL);
1295 } while(ret_val == 0);
1297 // report on the results
1301 // consider timeout to be fatal and cancel validation
1302 return -2; //MVALID_STATUS_UNKNOWN;
1306 return MVALID_STATUS_INVALID;
1310 return MVALID_STATUS_VALID;
1317 // special return values :
1321 int multi_fs_tracker_validate_mission_normal()
1323 switch(ValidateMission(NULL)){
1341 // return an MVALID_STATUS_* (see multiui.h) value, or -2 if the user has "cancelled"
1342 int multi_fs_tracker_validate_mission(char *filename)
1344 vmt_validate_mission_req_struct mission;
1345 char popup_string[512] = "";
1347 if(!Multi_fs_tracker_inited){
1348 return MVALID_STATUS_UNKNOWN;
1351 // get the checksum of the local file
1352 memset(&mission, 0, sizeof(mission));
1353 SDL_strlcpy(mission.file_name, filename, SDL_arraysize(mission.file_name));
1354 if(!cf_chksum_long(mission.file_name, (uint*)&mission.checksum)){
1355 return MVALID_STATUS_UNKNOWN;
1358 // try and validate the mission
1359 if(ValidateMission(&mission) != 0){
1360 return MVALID_STATUS_UNKNOWN;
1363 // do frames for standalone and non-standalone
1364 if(Game_mode & GM_STANDALONE_SERVER){
1367 // set the filename in the dialog
1368 std_gen_set_text(filename, 2);
1370 // validate the mission
1371 ret_code = multi_fs_tracker_validate_mission_std();
1373 // if the dialog is no longer active, cancel everything
1374 //if(!std_gen_is_active()){
1375 // return MVALID_STATUS_UNKNOWN;
1380 SDL_snprintf(popup_string, SDL_arraysize(popup_string), XSTR("Validating mission %s", 1074), filename);
1383 switch(popup_till_condition(multi_fs_tracker_validate_mission_normal, XSTR("&Cancel", 667), popup_string)){
1386 // bash some API values here so that next time we try and verify, everything works
1387 extern int MissionValidState;
1388 MissionValidState = VALID_STATE_IDLE;
1393 return MVALID_STATUS_UNKNOWN;
1397 return MVALID_STATUS_INVALID;
1401 return MVALID_STATUS_VALID;
1405 return MVALID_STATUS_UNKNOWN;
1408 // report on the results of the stats store procedure
1409 void multi_fs_tracker_report_stats_results()
1414 // tell everyone stats store is complete
1415 SDL_strlcpy(str, XSTR("<PXO stats store process complete>", 1001), SDL_arraysize(str));
1416 send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL, NULL, 1);
1417 multi_display_chat_msg(str, 0, 0);
1421 for(idx=0; idx<MAX_PLAYERS; idx++){
1422 // for all players who we care about
1423 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
1424 // if the stats get or send failed for any player, report as such
1425 if(((Net_players[idx].tracker_player_id <= 0) || (Net_players[idx].flags & NETINFO_FLAG_MT_GET_FAILED) || (Net_players[idx].flags & NETINFO_FLAG_MT_SEND_FAILED) || !(Net_players[idx].flags & NETINFO_FLAG_MT_DONE)) && (Net_players[idx].player != NULL)){
1426 SDL_snprintf(str, SDL_arraysize(str), XSTR("<PXO stats store failed for player %s>", 1002), Net_players[idx].player->callsign);
1427 send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL, NULL, 1);
1429 multi_display_chat_msg(str, 0, 0);
1436 // return an MSW_STATUS_* constant
1437 int multi_fs_tracker_validate_sw_std()
1441 // wait for a response from the tracker
1443 ret_val = ValidateSquadWar(NULL, &Multi_tracker_sw_response);
1444 } while(ret_val == 0);
1446 // report on the results
1450 return MVALID_STATUS_UNKNOWN;
1454 return MVALID_STATUS_INVALID;
1458 return MVALID_STATUS_VALID;
1461 return MVALID_STATUS_UNKNOWN;
1464 // special return values :
1468 int multi_fs_tracker_validate_sw_normal()
1470 switch(ValidateSquadWar(NULL, &Multi_tracker_sw_response)){
1488 #define STUFF_SW_RESPONSE(_c, _len) do {\
1489 SDL_strlcpy(_c, "", _len);\
1492 for(_idx=0; _idx<MAX_SQUAD_RESPONSE_LEN; _idx++){\
1493 if(Multi_tracker_sw_response.reason[_idx] == '\0'){\
1499 SDL_strlcpy(_c, Multi_tracker_sw_response.reason, _len);\
1503 // return an MSW_STATUS_* value
1504 int multi_fs_tracker_validate_sw(squad_war_request *sw_req, char *bad_reply, const int max_reply_len)
1506 char popup_string[512] = "";
1508 if(!Multi_fs_tracker_inited){
1509 return MSW_STATUS_UNKNOWN;
1512 // zero the response
1513 memset(&Multi_tracker_sw_response, 0, sizeof(Multi_tracker_sw_response));
1515 // try and validate the mission
1516 if(ValidateSquadWar(sw_req, &Multi_tracker_sw_response) != 0){
1517 SDL_strlcpy(bad_reply, "Error sending request for Squad War validation", max_reply_len);
1519 return MSW_STATUS_UNKNOWN;
1522 // do frames for standalone and non-standalone
1523 if(Game_mode & GM_STANDALONE_SERVER){
1526 // validate the mission
1527 ret_code = multi_fs_tracker_validate_sw_std();
1529 // copy the return code
1530 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1534 SDL_strlcpy(popup_string, XSTR("Validating squad war", 1075), SDL_arraysize(popup_string));
1537 switch(popup_till_condition(multi_fs_tracker_validate_sw_normal, XSTR("&Cancel", 645), popup_string)){
1541 // bash some API values here so that next time we try and verify, everything works
1542 extern int SquadWarValidState;
1543 SquadWarValidState = VALID_STATE_IDLE;
1545 // copy the return code
1546 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1551 // copy the return code
1552 SDL_strlcpy(bad_reply, "Timeout", max_reply_len);
1553 return MSW_STATUS_UNKNOWN;
1557 // copy the return code
1558 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1559 return MSW_STATUS_INVALID;
1563 // copy the return code
1564 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1565 return MSW_STATUS_VALID;
1569 SDL_strlcpy(bad_reply, "Unknown error", max_reply_len);
1570 return MSW_STATUS_UNKNOWN;
1573 // popup do function
1574 // -3 Error -- Called with NULL, but no request is waiting
1575 // -2 Error -- Already sending data (hasn't timed out yet)
1576 // -1 Timeout trying to send pilot data
1578 // 1 Data succesfully sent
1579 // 2 Send Cancelled (data may still have been written already, we just haven't been ACK'd yet)
1580 // 3 Pilot not written (for some reason)
1582 int multi_fs_tracker_store_sw_do()
1584 switch(SendSWData(NULL, &Multi_tracker_sw_response)){
1602 // store the results of a squad war mission on PXO, return 1 on success
1603 int multi_fs_tracker_store_sw(squad_war_result *sw_res, char *bad_reply, const int max_reply_len)
1605 char popup_string[512] = "";
1607 // clear any old requests
1608 SendSWData((squad_war_result*)0xffffffff, NULL);
1610 // send this new request
1611 SendSWData(sw_res, &Multi_tracker_sw_response);
1614 if(Game_mode & GM_STANDALONE_SERVER){
1617 ret_code = SendSWData(NULL, &Multi_tracker_sw_response);
1618 } while(ret_code == 0);
1627 SDL_strlcpy(popup_string, XSTR("Storing SquadWar results", 1078), SDL_arraysize(popup_string));
1629 // wait for a response
1630 if(popup_till_condition(multi_fs_tracker_store_sw_do, XSTR("&Cancel", 645), popup_string) == 10){