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_gui.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()
372 PSNET_TOP_LAYER_PROCESS();
374 if(Multi_fs_tracker_inited){
375 // pilot validation system
378 // pilot tracing system
381 // game tracking system
384 // set if we've got any pending game list items
387 multi_fs_tracker_process_game_item(gl);
393 // initialize the master tracker API for Freespace
394 void multi_fs_tracker_init()
396 // don't do anything if we're already initialized
397 if(Multi_fs_tracker_inited){
401 // initialize the low-level validation stuff
402 if(!InitValidateClient()){
403 ml_printf("Error initializing tracker api (validateclient)\n");
407 // initialize the low-level pilot tracking stuff
408 if(!InitPilotTrackerClient()){
409 ml_printf("Error initializing tracker api (pilotclient)\n");
413 // intialize the low-level game tracking stuff
415 if(!InitGameTrackerClient(GT_FREESPACE2)){
417 if(!InitGameTrackerClient(GT_FREESPACE)){
419 ml_printf("Error initializing tracker api (gameclient)\n");
423 nprintf(("Network","Successfully initialized tracker api\n"));
425 // we've successfully initialized the tracker stuff
426 Multi_fs_tracker_inited = 1;
429 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
430 int multi_fs_tracker_validate(int show_error)
432 validate_id_request vir;
434 if(!Multi_fs_tracker_inited){
435 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Warning, Parallax Online startup failed. Will not be able to play tracker games!",666));
439 // set this to false for now
440 Multi_tracker_player_is_valid = 0;
441 Multi_tracker_got_response = 0;
443 // mark the module as busy
444 Multi_fs_tracker_busy = 1;
447 // validate our pilot on the master tracker if possible
448 memset(&vir,0,sizeof(vir));
449 SDL_zero(Multi_tracker_id_string);
450 SDL_strlcpy(vir.login, Multi_tracker_login, SDL_arraysize(vir.login));
451 SDL_strlcpy(vir.password, Multi_tracker_passwd, SDL_arraysize(vir.password));
452 ValidateUser(&vir,Multi_tracker_id_string);
454 // set validation mode
455 Multi_validate_mode = 0;
457 int rval = popup_till_condition(multi_fs_validate_process,XSTR("&Cancel",667),XSTR("Attempting to validate pilot ...",668));
459 // if we failed for one reason or another
460 case MT_VALIDATE_FAIL :
461 // if we're supposed to show error codes
463 popup(PF_USE_AFFIRMATIVE_ICON | PF_BODY_BIG,1,XSTR("&Ok",669),XSTR("Pilot rejected by Parallax Online!",670));
466 Multi_validate_mode = -1;
468 Multi_fs_tracker_busy = 0;
471 case MT_VALIDATE_SUCCEED :
473 if(Multi_tracker_fs_pilot.virgin_pilot){
474 multi_common_add_notify(XSTR("Successfully created and validated new pilot!",671));
476 multi_common_add_notify(XSTR("Parallax Online pilot validation succeeded!",672));
479 // copy my statistics into my pilot file
480 multi_stats_tracker_to_fs(&Multi_tracker_fs_pilot,&Player->stats);
482 Multi_validate_mode = -1;
484 Multi_fs_tracker_busy = 0;
487 case MT_VALIDATE_TIMEOUT :
488 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));
490 // if the user clicked abort, then leave. otherwise try again
492 Multi_validate_mode = -1;
494 Multi_fs_tracker_busy = 0;
500 Multi_validate_mode = -1;
502 Multi_fs_tracker_busy = 0;
504 // essentially, cancel
510 // attempt to log the current game server in with the master tracker
511 void multi_fs_tracker_login_freespace()
513 if(!Multi_fs_tracker_inited){
514 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Warning, Parallax Online startup failed. Will not be able to play tracker games!",666));
518 // if we're already logged into a game, don't do anything
519 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) || (Net_player->flags & NETINFO_FLAG_MT_CONNECTED) ) {
523 // pretty much all we do is make 1 call
524 memset(&Multi_tracker_game_data, 0, sizeof(Multi_tracker_game_data));
525 SDL_strlcpy(Multi_tracker_game_data.game_name, Netgame.name, SDL_arraysize(Multi_tracker_game_data.game_name));
526 Multi_tracker_game_data.difficulty = 99;
527 Multi_tracker_game_data.type = 0;
528 Multi_tracker_game_data.state = 1;
529 Multi_tracker_game_data.max_players = 12;
530 Multi_tracker_game_data.current_num_players = 0;
532 // if we have a valid channel string, use it
533 if(strlen(Multi_fs_tracker_channel)){
534 SDL_strlcpy(Multi_tracker_game_data.channel, Multi_fs_tracker_channel, SDL_arraysize(Multi_tracker_game_data.channel));
537 StartTrackerGame(&Multi_tracker_game_data);
538 Net_player->flags |= NETINFO_FLAG_MT_CONNECTED;
541 ml_string(NOX("Server connected to Game Tracker"));
544 // attempt to update all player statistics and scores on the tracker
545 int multi_fs_tracker_store_stats()
550 ml_string(NOX("Server storing stats on User Tracker"));
552 // retrieve stats from tracker
553 Multi_store_stats_mode = MT_STORE_STATS_VALIDATE;
555 // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
556 Multi_store_stats_player_index = -1;
557 Multi_store_stats_player_flag = 1;
559 // mark the module as busy
560 Multi_fs_tracker_busy = 1;
562 // unmark everyone's GET_FAILED flag
563 for(idx=0;idx<MAX_PLAYERS;idx++){
564 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_GET_FAILED);
565 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_SEND_FAILED);
566 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_DONE);
570 // if playing with an invalid ships.tbl
571 if(!Game_ships_tbl_valid){
572 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);
573 multi_display_chat_msg(XSTR("<Server detected a hacked ships.tbl. Stats will not be saved>", 1044), 0, 0);
574 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You are playing with a hacked ships.tbl, your stats will not be saved", 1045) );
575 multi_fs_tracker_report_stats_results();
576 Multi_fs_tracker_busy = 0;
580 // if playing with an invalid weapons.tbl
581 if(!Game_weapons_tbl_valid){
582 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);
583 multi_display_chat_msg(XSTR("<Server detected a hacked weapons.tbl. Stats will not be saved>", 1046), 0, 0);
584 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You are playing with a hacked weapons.tbl, your stats will not be saved", 1047) );
585 multi_fs_tracker_report_stats_results();
586 Multi_fs_tracker_busy = 0;
590 // if there is only 1 player, don't store the stats
591 if((multi_num_players() <= 1) && (Multi_num_players_at_start <= 1)){
592 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);
593 multi_display_chat_msg(XSTR("<Not enough players were present at game start or end, stats will not be saved>", 1048), 0, 0);
594 multi_fs_tracker_report_stats_results();
595 Multi_fs_tracker_busy = 0;
599 // if any players have hacked info
600 for(idx=0; idx<MAX_PLAYERS; idx++){
601 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
606 // check to see if the mission is valid
607 if(multi_fs_tracker_validate_mission(Game_current_mission_filename) != MVALID_STATUS_VALID){
608 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);
609 multi_display_chat_msg(XSTR("<Server detected a non PXO validated mission. Stats will not be saved>", 1049), 0, 0);
610 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK, XSTR("This is not a PXO validated mission, your stats will not be saved", 1050));
611 Multi_fs_tracker_busy = 0;
616 popup_till_condition(multi_fs_store_stats_do,XSTR("&Cancel",667), XSTR("Sending player stats requests ...",676));
618 // send appropriate chat messages indicating stats store failure
619 multi_fs_tracker_report_stats_results();
621 // mark the module as not busy anymore
622 Multi_fs_tracker_busy = 0;
627 // attempt to update all player statistics (standalone mode)
628 int multi_fs_std_tracker_store_stats()
633 // don't do anything if this is a tracker game
634 if(!(MULTI_IS_TRACKER_GAME)){
638 if(!Multi_fs_tracker_inited){
643 ml_string(NOX("Standalone server storing stats on User Tracker"));
645 // retrieve stats from tracker
646 Multi_store_stats_mode = MT_STORE_STATS_VALIDATE;
648 // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
649 Multi_store_stats_player_index = -1;
650 Multi_store_stats_player_flag = 1;
652 // mark the module as busy
653 Multi_fs_tracker_busy = 1;
655 // unmark everyone's GET_FAILED flag
656 for(idx=0;idx<MAX_PLAYERS;idx++){
657 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_GET_FAILED);
658 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_SEND_FAILED);
659 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_DONE);
663 // if playing with an invalid ships.tbl
664 if(!Game_ships_tbl_valid){
665 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);
666 multi_display_chat_msg(XSTR("<Server detected a hacked ships.tbl. Stats will not be saved>", 1044), 0, 0);
667 multi_fs_tracker_report_stats_results();
668 Multi_fs_tracker_busy = 0;
672 // if playing with an invalid weapons.tbl
673 if(!Game_weapons_tbl_valid){
674 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);
675 multi_display_chat_msg(XSTR("<Server detected a hacked weapons.tbl. Stats will not be saved>", 1046), 0, 0);
676 multi_fs_tracker_report_stats_results();
677 Multi_fs_tracker_busy = 0;
681 // if any players have hacked info
682 for(idx=0; idx<MAX_PLAYERS; idx++){
683 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
688 // if there is only 1 player, don't store the stats
689 if((multi_num_players() <= 1) && (Multi_num_players_at_start <= 1)){
690 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);
691 multi_display_chat_msg(XSTR("<Not enough players were present at game start or end, stats will not be saved>", 1048), 0, 0);
692 multi_fs_tracker_report_stats_results();
693 Multi_fs_tracker_busy = 0;
697 // check to see if the mission is valid
698 if(multi_fs_tracker_validate_mission(Game_current_mission_filename) != MVALID_STATUS_VALID){
699 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);
700 multi_display_chat_msg(XSTR("<Server detected a non PXO validated mission. Stats will not be saved>", 1049), 0, 0);
701 Multi_fs_tracker_busy = 0;
706 // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
708 ret_val = multi_fs_store_stats_do();
709 game_set_frametime(GS_STATE_STANDALONE_POSTGAME);
711 } while(ret_val == MT_STATS_NOT_DONE);
713 // report on the results
714 multi_fs_tracker_report_stats_results();
716 // mark the module as no longer busy
717 Multi_fs_tracker_busy = 0;
722 // log freespace out of the tracker
723 void multi_fs_tracker_logout()
725 if(!Multi_fs_tracker_inited){
729 // make sure we're connected
730 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER) || !(Net_player->flags & NETINFO_FLAG_MT_CONNECTED)){
734 // otherwise, log us out
738 memset(&Multi_tracker_game_data, 0, sizeof(Multi_tracker_game_data));
739 Net_player->flags &= ~(NETINFO_FLAG_MT_CONNECTED);
742 ml_string(NOX("Server disconnecting from Game Tracker"));
745 // send a request for a list of games
746 void multi_fs_tracker_send_game_request()
748 filter_game_list_struct filter;
751 // if we're not initialized, don't do anything
752 if(!Multi_fs_tracker_inited){
756 // if we have a valid filter, use that instead
757 len = strlen(Multi_fs_tracker_filter);
758 if((len > 0) && (len < CHANNEL_LEN-1) ){
759 memset(&filter,0,sizeof(filter_game_list_struct));
761 SDL_strlcpy(filter.channel, Multi_fs_tracker_filter, SDL_arraysize(filter.channel));
762 RequestGameListWithFilter(&filter);
769 // if the API has successfully been initialized and is running
770 int multi_fs_tracker_inited()
772 return Multi_fs_tracker_inited;
775 // update our settings on the tracker regarding the current netgame stuff
776 void multi_fs_tracker_update_game(netgame_info *ng)
778 if(!Multi_fs_tracker_inited){
782 // copy in the relevant data
783 Multi_tracker_game_data.max_players = ng->max_players;
784 Multi_tracker_game_data.current_num_players = multi_num_players();
787 memset(Multi_tracker_game_data.players, 0 ,MAX_FREESPACE_PLAYERS * MAX_FREESPACE_PLAYER_NAME_LEN);
789 for(idx=0;idx<MAX_PLAYERS;idx++){
790 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
791 strcpy(Multi_tracker_game_data.players[count], Net_players[idx].player->callsign);
792 Multi_tracker_game_data.player_rank[count] = Net_players[idx].player->stats.rank;
798 SDL_strlcpy(Multi_tracker_game_data.mission_name, ng->name, SDL_arraysize(Multi_tracker_game_data.mission_name));
801 ml_string(NOX("Server updating netgame info for Game Tracker"));
804 // if we're currently busy performing some tracker operation (ie, you should wait or not)
805 int multi_fs_tracker_busy()
807 return Multi_fs_tracker_busy;
811 // -----------------------------------------------------------------------------------
812 // FREESPACE MASTER TRACKER FORWARD DEFINITIONS
815 // used with popup_till_condition() for validating freespace pilots
816 int multi_fs_validate_process()
818 // should never be here if this is not true
819 SDL_assert(Multi_fs_tracker_inited);
821 PSNET_TOP_LAYER_PROCESS();
823 // if we're still in player validation mode
824 if(Multi_validate_mode == 0){
825 switch(ValidateUser(NULL,NULL)){
826 // timeout on waiting for response
828 return MT_VALIDATE_TIMEOUT;
832 // set tracker id to -1
833 SDL_strlcpy(Multi_tracker_id_string, "-1", SDL_arraysize(Multi_tracker_id_string));
834 Multi_tracker_id = -1;
835 return MT_VALIDATE_FAIL;
839 return MT_VALIDATE_NOT_DONE;
843 // now we need to try and receive stats
845 // mark me as being valid
846 Multi_tracker_player_is_valid = 1;
848 // change the popup text
849 popup_change_text(XSTR("Attempting to get pilot stats ...",679));
851 // get my tracker id#
852 Multi_tracker_id = atoi(Multi_tracker_id_string);
853 SDL_assert(Multi_tracker_id != -1);
855 GetFSPilotData((vmt_stats_struct*)0xffffffff,NULL,NULL,0);
856 GetFSPilotData(&Multi_tracker_fs_pilot,Player->callsign,Multi_tracker_id_string,1);
859 Multi_validate_mode = 1;
860 return MT_VALIDATE_NOT_DONE;
866 switch(GetFSPilotData(NULL,NULL,NULL,0)){
869 return MT_VALIDATE_TIMEOUT;
873 return MT_VALIDATE_NOT_DONE;
877 return MT_VALIDATE_SUCCEED;
881 return MT_VALIDATE_FAIL;
885 // we're not done yet - probably should never get here
886 return MT_VALIDATE_NOT_DONE;
889 // used with popup_till_condition() for storing player stats at the end of a freespace game
890 int multi_fs_store_stats_do()
892 char tracker_id_string[512];
893 char popup_text[100];
895 SDL_assert(Multi_fs_tracker_inited);
897 PSNET_TOP_LAYER_PROCESS();
899 switch(Multi_store_stats_mode){
900 // get stats for all players
901 case MT_STORE_STATS_VALIDATE :
902 Multi_store_stats_mode = MT_STORE_STATS_GET_STATS;
905 case MT_STORE_STATS_GET_STATS:
906 // if we need to get the next player
907 if(Multi_store_stats_player_flag){
908 Multi_store_stats_player_index = multi_fs_store_stats_get_next_player(Multi_store_stats_player_index);
910 // if it returns < 0 we're done with all players and should move onto the next stage (applying mission stats)
911 if(Multi_store_stats_player_index < 0){
912 Multi_store_stats_mode = MT_STORE_STATS_ACCEPT;
913 return MT_STATS_NOT_DONE;
916 // unset this flag so we process the request
917 Multi_store_stats_player_flag = 0;
919 // fill out the information request
920 memset(tracker_id_string,0,512);
921 SDL_assert(Net_players[Multi_store_stats_player_index].tracker_player_id > 0);
923 // verify that there are no duplicate tracker id's to this one
924 multi_fs_tracker_check_dup(Net_players[Multi_store_stats_player_index].tracker_player_id,Multi_store_stats_player_index);
925 multi_fs_tracker_check_dup_callsign(&Net_players[Multi_store_stats_player_index],Multi_store_stats_player_index);
927 SDL_snprintf(tracker_id_string, SDL_arraysize(tracker_id_string), "%d", Net_players[Multi_store_stats_player_index].tracker_player_id);
928 Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = -1;
929 Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = 0;
931 // send the request itself
932 GetFSPilotData((vmt_stats_struct*)0xffffffff, NULL, NULL,0);
933 memset(&Multi_store_stats_stats, 0, sizeof(Multi_store_stats_stats));
934 if(GetFSPilotData(&Multi_store_stats_stats, Net_players[Multi_store_stats_player_index].player->callsign,tracker_id_string,1) != 0){
937 // move onto the next player
938 Multi_store_stats_player_flag = 1;
939 return MT_STATS_NOT_DONE;
942 // set the popup text
943 if(!(Game_mode & GM_STANDALONE_SERVER)){
944 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);
945 popup_change_text(popup_text);
947 return MT_STATS_NOT_DONE;
950 // process the request
951 switch(GetFSPilotData(NULL,NULL,NULL,0)){
954 // copy his stats, then flag him as done so we move onto the next guys
955 multi_stats_tracker_to_fs(&Multi_store_stats_stats,&Net_players[Multi_store_stats_player_index].player->stats);
957 // make sure we apply his mission stats now
958 scoring_do_accept(&Net_players[Multi_store_stats_player_index].player->stats);
962 // debug code to check for bogus stats
963 scoring_struct *ssp = &(Net_players[Multi_store_stats_player_index].player->stats);
964 vmt_stats_struct *vmt = &Multi_store_stats_stats;
966 if ( (ssp->missions_flown < vmt->missions_flown) || (ssp->flight_time < ssp->flight_time) || (ssp->kill_count < vmt->kill_count) ) {
972 // flag him as being completed
973 Multi_store_stats_player_flag = 1;
975 // also store this last security value so we can properly update him
976 Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = Multi_store_stats_stats.security;
977 Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = Multi_store_stats_stats.checksum;
985 case 3: case -2: case 2: case -3: case -1:
986 // this shouldn't be happening under most conditions. For debugging....
989 // flag him as done so we move onto the next guy
990 Multi_store_stats_player_flag = 1;
991 Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = -1;
992 Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = 0;
994 // mark down that the stats get for him failed
995 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_GET_FAILED;
996 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1001 // update all stats for all players locally and on client machines
1002 case MT_STORE_STATS_ACCEPT :
1003 // tell everyone to save their stats
1004 send_store_stats_packet(1);
1006 // reset status flags and indices
1007 Multi_store_stats_player_index = -1;
1008 Multi_store_stats_player_flag = 1;
1010 Multi_store_stats_mode = MT_STORE_STATS_SEND_STATS;
1013 // send stats to the tracker
1014 case MT_STORE_STATS_SEND_STATS:
1015 // if we need to get the next player
1016 if(Multi_store_stats_player_flag){
1017 Multi_store_stats_player_index = multi_fs_store_stats_get_next_player(Multi_store_stats_player_index);
1019 // if it returns < 0 we need to move onto the next player
1020 if(Multi_store_stats_player_index < 0){
1021 return MT_STATS_SUCCEED;
1024 Multi_store_stats_player_flag = 0;
1026 // fill in the information
1027 memset(&Multi_store_stats_stats,0,sizeof(Multi_store_stats_stats));
1029 SDL_assert(Net_players[Multi_store_stats_player_index].tracker_player_id > 0);
1031 // verify that there are no duplicate tracker id's to this one
1032 multi_fs_tracker_check_dup(Net_players[Multi_store_stats_player_index].tracker_player_id,Multi_store_stats_player_index);
1033 multi_fs_tracker_check_dup_callsign(&Net_players[Multi_store_stats_player_index],Multi_store_stats_player_index);
1034 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);
1036 Multi_store_stats_stats.security = Net_players[Multi_store_stats_player_index].s_info.tracker_security_last;
1038 // SDL_assert(Net_players[Multi_store_stats_player_index].s_info.tracker_checksum != 0);
1039 Multi_store_stats_stats.checksum = Net_players[Multi_store_stats_player_index].s_info.tracker_checksum;
1042 SendFSPilotData((vmt_stats_struct*)0xffffffff);
1043 if(SendFSPilotData(&Multi_store_stats_stats) != 0){
1046 // failed to send, try another player the next time around
1047 Multi_store_stats_player_flag = 1;
1048 return MT_STATS_NOT_DONE;
1051 // set the popup text
1052 if(!(Game_mode & GM_STANDALONE_SERVER)){
1053 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);
1054 popup_change_text(popup_text);
1057 return MT_STATS_NOT_DONE;
1060 // otherwise check on his status
1061 switch(SendFSPilotData(NULL)){
1063 case -1: case -2: case -3: case 2: case 3:
1064 // flag him as done so we move onto the next guy
1065 Multi_store_stats_player_flag = 1;
1067 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_SEND_FAILED;
1068 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1073 // flag him as done so we move onto the next guys
1074 Multi_store_stats_player_flag = 1;
1075 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1082 // return not done yet
1083 return MT_STATS_NOT_DONE;
1086 // copy a freespace stats struct to a tracker-freespace stats struct
1087 void multi_stats_fs_to_tracker(scoring_struct *fs, vmt_stats_struct *vmt, player *pl, int tracker_id)
1089 char tracker_id_string[256];
1091 // tracker id string
1092 SDL_snprintf(tracker_id_string, SDL_arraysize(tracker_id_string),"%d", tracker_id);
1093 SDL_strlcpy(vmt->tracker_id, tracker_id_string, SDL_arraysize(vmt->tracker_id));
1096 SDL_strlcpy(vmt->pilot_name, pl->callsign, SDL_arraysize(vmt->pilot_name));
1098 // score, rank and medals
1099 vmt->score = fs->score;
1100 vmt->rank = fs->rank;
1102 SDL_assert(MAX_FS2_MEDALS == NUM_MEDALS);
1103 memcpy(vmt->medals, fs->medals, sizeof(int) * MAX_FS2_MEDALS);
1104 vmt->num_medals = MAX_FS2_MEDALS;
1106 memcpy(vmt->medals, fs->medals, sizeof(int) * MAX_FS_MEDALS);
1109 // kills and assists
1111 SDL_assert(MAX_FS2_SHIP_TYPES == MAX_SHIP_TYPES);
1112 memcpy(vmt->kills, fs->kills, sizeof(ushort) * MAX_FS2_SHIP_TYPES);
1113 vmt->num_ship_types = MAX_FS2_SHIP_TYPES;
1115 memcpy(vmt->kills, fs->kills, sizeof(int) * MAX_FS_SHIP_TYPES);
1117 vmt->assists = fs->assists;
1118 vmt->kill_count = fs->kill_count;
1119 vmt->kill_count_ok = fs->kill_count_ok;
1123 vmt->p_shots_fired = fs->p_shots_fired;
1124 vmt->s_shots_fired = fs->s_shots_fired;
1125 vmt->p_shots_hit = fs->p_shots_hit;
1126 vmt->s_shots_hit = fs->s_shots_hit;
1127 vmt->p_bonehead_hits = fs->p_bonehead_hits;
1128 vmt->s_bonehead_hits = fs->s_bonehead_hits;
1129 vmt->bonehead_kills = fs->bonehead_kills;
1131 // missions flown information
1132 vmt->missions_flown = fs->missions_flown;
1133 vmt->flight_time = fs->flight_time;
1134 vmt->last_flown = (unsigned int)fs->last_flown;
1137 // copy a tracker-freespace stats struct to a freespace stats struct
1138 void multi_stats_tracker_to_fs(vmt_stats_struct *vmt,scoring_struct *fs)
1141 int num_medals, num_ship_types;
1144 // score, rank and medals
1145 fs->score = vmt->score;
1146 fs->rank = vmt->rank;
1148 num_medals = vmt->num_medals;
1149 if(num_medals > NUM_MEDALS){
1151 num_medals = NUM_MEDALS;
1153 memset(fs->medals, 0, sizeof(int) * NUM_MEDALS);
1154 memcpy(fs->medals, vmt->medals, sizeof(int) * num_medals);
1156 SDL_zero(fs->medals);
1157 memcpy(fs->medals, vmt->medals, sizeof(int) * NUM_MEDALS);
1160 // kills and assists
1162 num_ship_types = vmt->num_ship_types;
1163 if(num_ship_types > MAX_SHIP_TYPES){
1165 num_ship_types = MAX_SHIP_TYPES;
1167 memset(fs->kills, 0, sizeof(ushort) * MAX_SHIP_TYPES);
1168 memcpy(fs->kills, vmt->kills, sizeof(ushort) * num_ship_types);
1170 SDL_zero(fs->kills);
1171 memcpy(fs->kills, vmt->kills, sizeof(int) * MAX_SHIP_TYPES);
1173 fs->assists = vmt->assists;
1174 fs->kill_count = vmt->kill_count;
1175 fs->kill_count_ok = vmt->kill_count_ok;
1178 fs->p_shots_fired = vmt->p_shots_fired;
1179 fs->s_shots_fired = vmt->s_shots_fired;
1180 fs->p_shots_hit = vmt->p_shots_hit;
1181 fs->s_shots_hit = vmt->s_shots_hit;
1182 fs->p_bonehead_hits = vmt->p_bonehead_hits;
1183 fs->s_bonehead_hits = vmt->s_bonehead_hits;
1184 fs->bonehead_kills = vmt->bonehead_kills;
1186 // missions flown information
1187 fs->missions_flown = vmt->missions_flown;
1188 fs->flight_time = vmt->flight_time;
1189 fs->last_flown = (fs_time_t)vmt->last_flown;
1190 if(fs->last_flown < 0){
1195 // process an incoming active game item
1196 void multi_fs_tracker_process_game_item(game_list *gl)
1201 for(idx=0;idx<MAX_GAME_LISTS_PER_PACKET;idx++){
1202 // skip null server addresses
1203 if(gl->game_server[idx] == 0){
1207 // package up the game information
1208 memset(&ag,0,sizeof(active_game));
1209 SDL_strlcpy(ag.name, gl->game_name[idx], SDL_arraysize(ag.name));
1210 memcpy(&ag.server_addr.addr[0], &gl->game_server[idx], IP_ADDRESS_LENGTH);
1211 ag.server_addr.type = NET_TCP;
1212 ag.server_addr.port = DEFAULT_GAME_PORT;
1214 // add to the active game list
1215 // multi_update_active_games(&ag);
1217 // query this server
1218 send_server_query(&ag.server_addr);
1222 int multi_fs_store_stats_get_next_player(int cur_player)
1226 // if we're at the end of the list
1227 if(cur_player == (MAX_PLAYERS - 1)){
1231 // find the next player
1232 for(idx=cur_player+1;idx<MAX_PLAYERS;idx++){
1234 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) ){
1239 // couldn't find one
1243 // verify that there are no duplicate tracker id's to this one
1244 void multi_fs_tracker_check_dup(int tracker_id,int player_index)
1248 // compare against all players except the passed player_index
1249 for(idx=0;idx<MAX_PLAYERS;idx++){
1250 if(idx == player_index){
1254 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].tracker_player_id != -1)){
1255 SDL_assert(Net_players[idx].tracker_player_id != tracker_id);
1260 // verify that there are no duplicate pilot callsigns
1261 void multi_fs_tracker_check_dup_callsign(net_player *player,int player_index)
1265 // compare against all players except the passed player_index
1266 for(idx=0;idx<MAX_PLAYERS;idx++){
1267 if(idx == player_index){
1271 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].tracker_player_id != -1)){
1272 SDL_assert(strcmp(player->player->callsign,Net_players[idx].player->callsign));
1277 // return an MVALID_STATUS_* constant
1278 int multi_fs_tracker_validate_mission_std()
1282 // wait for a response from the tracker
1284 ret_val = ValidateMission(NULL);
1285 } while(ret_val == 0);
1287 // report on the results
1291 std_destroy_gen_dialog();
1292 return MVALID_STATUS_UNKNOWN;
1296 std_destroy_gen_dialog();
1297 return MVALID_STATUS_INVALID;
1301 std_destroy_gen_dialog();
1302 return MVALID_STATUS_VALID;
1309 // special return values :
1313 int multi_fs_tracker_validate_mission_normal()
1315 switch(ValidateMission(NULL)){
1333 // return an MVALID_STATUS_* (see multiui.h) value, or -2 if the user has "cancelled"
1334 int multi_fs_tracker_validate_mission(char *filename)
1336 vmt_validate_mission_req_struct mission;
1337 char popup_string[512] = "";
1339 if(!Multi_fs_tracker_inited){
1340 return MVALID_STATUS_UNKNOWN;
1343 // get the checksum of the local file
1344 memset(&mission, 0, sizeof(mission));
1345 SDL_strlcpy(mission.file_name, filename, SDL_arraysize(mission.file_name));
1346 if(!cf_chksum_long(mission.file_name, (uint*)&mission.checksum)){
1347 return MVALID_STATUS_UNKNOWN;
1350 // try and validate the mission
1351 if(ValidateMission(&mission) != 0){
1352 return MVALID_STATUS_UNKNOWN;
1355 // do frames for standalone and non-standalone
1356 if(Game_mode & GM_STANDALONE_SERVER){
1359 // set the filename in the dialog
1360 std_gen_set_text(filename, 2);
1362 // validate the mission
1363 ret_code = multi_fs_tracker_validate_mission_std();
1365 // if the dialog is no longer active, cancel everything
1366 //if(!std_gen_is_active()){
1367 // return MVALID_STATUS_UNKNOWN;
1372 SDL_snprintf(popup_string, SDL_arraysize(popup_string), XSTR("Validating mission %s", 1074), filename);
1375 switch(popup_till_condition(multi_fs_tracker_validate_mission_normal, XSTR("&Cancel", 667), popup_string)){
1378 // bash some API values here so that next time we try and verify, everything works
1379 extern int MissionValidState;
1380 MissionValidState = VALID_STATE_IDLE;
1385 return MVALID_STATUS_UNKNOWN;
1389 return MVALID_STATUS_INVALID;
1393 return MVALID_STATUS_VALID;
1397 return MVALID_STATUS_UNKNOWN;
1400 // report on the results of the stats store procedure
1401 void multi_fs_tracker_report_stats_results()
1406 // tell everyone stats store is complete
1407 SDL_strlcpy(str, XSTR("<PXO stats store process complete>", 1001), SDL_arraysize(str));
1408 send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL, NULL, 1);
1409 multi_display_chat_msg(str, 0, 0);
1413 for(idx=0; idx<MAX_PLAYERS; idx++){
1414 // for all players who we care about
1415 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
1416 // if the stats get or send failed for any player, report as such
1417 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)){
1418 SDL_snprintf(str, SDL_arraysize(str), XSTR("<PXO stats store failed for player %s>", 1002), Net_players[idx].player->callsign);
1419 send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL, NULL, 1);
1421 multi_display_chat_msg(str, 0, 0);
1428 // return an MSW_STATUS_* constant
1429 int multi_fs_tracker_validate_sw_std()
1433 // wait for a response from the tracker
1435 ret_val = ValidateSquadWar(NULL, &Multi_tracker_sw_response);
1436 } while(ret_val == 0);
1438 // report on the results
1442 return MVALID_STATUS_UNKNOWN;
1446 return MVALID_STATUS_INVALID;
1450 return MVALID_STATUS_VALID;
1453 return MVALID_STATUS_UNKNOWN;
1456 // special return values :
1460 int multi_fs_tracker_validate_sw_normal()
1462 switch(ValidateSquadWar(NULL, &Multi_tracker_sw_response)){
1480 #define STUFF_SW_RESPONSE(_c, _len) do {\
1481 SDL_strlcpy(_c, "", _len);\
1484 for(_idx=0; _idx<MAX_SQUAD_RESPONSE_LEN; _idx++){\
1485 if(Multi_tracker_sw_response.reason[_idx] == '\0'){\
1491 SDL_strlcpy(_c, Multi_tracker_sw_response.reason, _len);\
1495 // return an MSW_STATUS_* value
1496 int multi_fs_tracker_validate_sw(squad_war_request *sw_req, char *bad_reply, const int max_reply_len)
1498 char popup_string[512] = "";
1500 if(!Multi_fs_tracker_inited){
1501 return MSW_STATUS_UNKNOWN;
1504 // zero the response
1505 memset(&Multi_tracker_sw_response, 0, sizeof(Multi_tracker_sw_response));
1507 // try and validate the mission
1508 if(ValidateSquadWar(sw_req, &Multi_tracker_sw_response) != 0){
1509 SDL_strlcpy(bad_reply, "Error sending request for Squad War validation", max_reply_len);
1511 return MSW_STATUS_UNKNOWN;
1514 // do frames for standalone and non-standalone
1515 if(Game_mode & GM_STANDALONE_SERVER){
1518 // validate the mission
1519 ret_code = multi_fs_tracker_validate_sw_std();
1521 // copy the return code
1522 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1526 SDL_strlcpy(popup_string, XSTR("Validating squad war", 1075), SDL_arraysize(popup_string));
1529 switch(popup_till_condition(multi_fs_tracker_validate_sw_normal, XSTR("&Cancel", 645), popup_string)){
1533 // bash some API values here so that next time we try and verify, everything works
1534 extern int SquadWarValidState;
1535 SquadWarValidState = VALID_STATE_IDLE;
1537 // copy the return code
1538 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1543 // copy the return code
1544 SDL_strlcpy(bad_reply, "Timeout", max_reply_len);
1545 return MSW_STATUS_UNKNOWN;
1549 // copy the return code
1550 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1551 return MSW_STATUS_INVALID;
1555 // copy the return code
1556 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1557 return MSW_STATUS_VALID;
1561 SDL_strlcpy(bad_reply, "Unknown error", max_reply_len);
1562 return MSW_STATUS_UNKNOWN;
1565 // popup do function
1566 // -3 Error -- Called with NULL, but no request is waiting
1567 // -2 Error -- Already sending data (hasn't timed out yet)
1568 // -1 Timeout trying to send pilot data
1570 // 1 Data succesfully sent
1571 // 2 Send Cancelled (data may still have been written already, we just haven't been ACK'd yet)
1572 // 3 Pilot not written (for some reason)
1574 int multi_fs_tracker_store_sw_do()
1576 switch(SendSWData(NULL, &Multi_tracker_sw_response)){
1594 // store the results of a squad war mission on PXO, return 1 on success
1595 int multi_fs_tracker_store_sw(squad_war_result *sw_res, char *bad_reply, const int max_reply_len)
1597 char popup_string[512] = "";
1599 // clear any old requests
1600 SendSWData((squad_war_result*)0xffffffff, NULL);
1602 // send this new request
1603 SendSWData(sw_res, &Multi_tracker_sw_response);
1606 if(Game_mode & GM_STANDALONE_SERVER){
1609 ret_code = SendSWData(NULL, &Multi_tracker_sw_response);
1610 } while(ret_code == 0);
1619 SDL_strlcpy(popup_string, XSTR("Storing SquadWar results", 1078), SDL_arraysize(popup_string));
1621 // wait for a response
1622 if(popup_till_condition(multi_fs_tracker_store_sw_do, XSTR("&Cancel", 645), popup_string) == 10){