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
414 if(!InitGameTrackerClient(GT_FREESPACE2)){
415 ml_printf("Error initializing tracker api (gameclient)\n");
419 nprintf(("Network","Successfully initialized tracker api\n"));
421 // we've successfully initialized the tracker stuff
422 Multi_fs_tracker_inited = 1;
425 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
426 int multi_fs_tracker_validate(int show_error)
428 validate_id_request vir;
430 if(!Multi_fs_tracker_inited){
431 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Warning, Parallax Online startup failed. Will not be able to play tracker games!",666));
435 // set this to false for now
436 Multi_tracker_player_is_valid = 0;
437 Multi_tracker_got_response = 0;
439 // mark the module as busy
440 Multi_fs_tracker_busy = 1;
443 // validate our pilot on the master tracker if possible
444 memset(&vir,0,sizeof(vir));
445 SDL_zero(Multi_tracker_id_string);
446 SDL_strlcpy(vir.login, Multi_tracker_login, SDL_arraysize(vir.login));
447 SDL_strlcpy(vir.password, Multi_tracker_passwd, SDL_arraysize(vir.password));
448 ValidateUser(&vir,Multi_tracker_id_string);
450 // set validation mode
451 Multi_validate_mode = 0;
453 int rval = popup_till_condition(multi_fs_validate_process,XSTR("&Cancel",667),XSTR("Attempting to validate pilot ...",668));
455 // if we failed for one reason or another
456 case MT_VALIDATE_FAIL :
457 // if we're supposed to show error codes
459 popup(PF_USE_AFFIRMATIVE_ICON | PF_BODY_BIG,1,XSTR("&Ok",669),XSTR("Pilot rejected by Parallax Online!",670));
462 Multi_validate_mode = -1;
464 Multi_fs_tracker_busy = 0;
467 case MT_VALIDATE_SUCCEED :
469 if(Multi_tracker_fs_pilot.virgin_pilot){
470 multi_common_add_notify(XSTR("Successfully created and validated new pilot!",671));
472 multi_common_add_notify(XSTR("Parallax Online pilot validation succeeded!",672));
475 // copy my statistics into my pilot file
476 multi_stats_tracker_to_fs(&Multi_tracker_fs_pilot,&Player->stats);
478 Multi_validate_mode = -1;
480 Multi_fs_tracker_busy = 0;
483 case MT_VALIDATE_TIMEOUT :
484 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));
486 // if the user clicked abort, then leave. otherwise try again
488 Multi_validate_mode = -1;
490 Multi_fs_tracker_busy = 0;
496 Multi_validate_mode = -1;
498 Multi_fs_tracker_busy = 0;
500 // essentially, cancel
506 // attempt to log the current game server in with the master tracker
507 void multi_fs_tracker_login_freespace()
509 if(!Multi_fs_tracker_inited){
510 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Warning, Parallax Online startup failed. Will not be able to play tracker games!",666));
514 // if we're already logged into a game, don't do anything
515 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) || (Net_player->flags & NETINFO_FLAG_MT_CONNECTED) ) {
519 // pretty much all we do is make 1 call
520 memset(&Multi_tracker_game_data, 0, sizeof(Multi_tracker_game_data));
521 SDL_strlcpy(Multi_tracker_game_data.game_name, Netgame.name, SDL_arraysize(Multi_tracker_game_data.game_name));
522 Multi_tracker_game_data.difficulty = 99;
523 Multi_tracker_game_data.type = 0;
524 Multi_tracker_game_data.state = 1;
525 Multi_tracker_game_data.max_players = 12;
526 Multi_tracker_game_data.current_num_players = 0;
528 // if we have a valid channel string, use it
529 if(strlen(Multi_fs_tracker_channel)){
530 SDL_strlcpy(Multi_tracker_game_data.channel, Multi_fs_tracker_channel, SDL_arraysize(Multi_tracker_game_data.channel));
533 StartTrackerGame(&Multi_tracker_game_data);
534 Net_player->flags |= NETINFO_FLAG_MT_CONNECTED;
537 ml_string(NOX("Server connected to Game Tracker"));
540 // attempt to update all player statistics and scores on the tracker
541 int multi_fs_tracker_store_stats()
546 ml_string(NOX("Server storing stats on User Tracker"));
548 // retrieve stats from tracker
549 Multi_store_stats_mode = MT_STORE_STATS_VALIDATE;
551 // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
552 Multi_store_stats_player_index = -1;
553 Multi_store_stats_player_flag = 1;
555 // mark the module as busy
556 Multi_fs_tracker_busy = 1;
558 // unmark everyone's GET_FAILED flag
559 for(idx=0;idx<MAX_PLAYERS;idx++){
560 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_GET_FAILED);
561 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_SEND_FAILED);
562 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_DONE);
566 // if playing with an invalid ships.tbl
567 if(!Game_ships_tbl_valid){
568 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);
569 multi_display_chat_msg(XSTR("<Server detected a hacked ships.tbl. Stats will not be saved>", 1044), 0, 0);
570 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You are playing with a hacked ships.tbl, your stats will not be saved", 1045) );
571 multi_fs_tracker_report_stats_results();
572 Multi_fs_tracker_busy = 0;
576 // if playing with an invalid weapons.tbl
577 if(!Game_weapons_tbl_valid){
578 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);
579 multi_display_chat_msg(XSTR("<Server detected a hacked weapons.tbl. Stats will not be saved>", 1046), 0, 0);
580 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You are playing with a hacked weapons.tbl, your stats will not be saved", 1047) );
581 multi_fs_tracker_report_stats_results();
582 Multi_fs_tracker_busy = 0;
586 // if there is only 1 player, don't store the stats
587 if((multi_num_players() <= 1) && (Multi_num_players_at_start <= 1)){
588 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);
589 multi_display_chat_msg(XSTR("<Not enough players were present at game start or end, stats will not be saved>", 1048), 0, 0);
590 multi_fs_tracker_report_stats_results();
591 Multi_fs_tracker_busy = 0;
595 // if any players have hacked info
596 for(idx=0; idx<MAX_PLAYERS; idx++){
597 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
602 // check to see if the mission is valid
603 if(multi_fs_tracker_validate_mission(Game_current_mission_filename) != MVALID_STATUS_VALID){
604 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);
605 multi_display_chat_msg(XSTR("<Server detected a non PXO validated mission. Stats will not be saved>", 1049), 0, 0);
606 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK, XSTR("This is not a PXO validated mission, your stats will not be saved", 1050));
607 Multi_fs_tracker_busy = 0;
612 popup_till_condition(multi_fs_store_stats_do,XSTR("&Cancel",667), XSTR("Sending player stats requests ...",676));
614 // send appropriate chat messages indicating stats store failure
615 multi_fs_tracker_report_stats_results();
617 // mark the module as not busy anymore
618 Multi_fs_tracker_busy = 0;
623 // attempt to update all player statistics (standalone mode)
624 int multi_fs_std_tracker_store_stats()
629 // don't do anything if this is a tracker game
630 if(!(MULTI_IS_TRACKER_GAME)){
634 if(!Multi_fs_tracker_inited){
639 ml_string(NOX("Standalone server storing stats on User Tracker"));
641 // retrieve stats from tracker
642 Multi_store_stats_mode = MT_STORE_STATS_VALIDATE;
644 // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
645 Multi_store_stats_player_index = -1;
646 Multi_store_stats_player_flag = 1;
648 // mark the module as busy
649 Multi_fs_tracker_busy = 1;
651 // unmark everyone's GET_FAILED flag
652 for(idx=0;idx<MAX_PLAYERS;idx++){
653 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_GET_FAILED);
654 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_SEND_FAILED);
655 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_DONE);
659 // if playing with an invalid ships.tbl
660 if(!Game_ships_tbl_valid){
661 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);
662 multi_display_chat_msg(XSTR("<Server detected a hacked ships.tbl. Stats will not be saved>", 1044), 0, 0);
663 multi_fs_tracker_report_stats_results();
664 Multi_fs_tracker_busy = 0;
668 // if playing with an invalid weapons.tbl
669 if(!Game_weapons_tbl_valid){
670 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);
671 multi_display_chat_msg(XSTR("<Server detected a hacked weapons.tbl. Stats will not be saved>", 1046), 0, 0);
672 multi_fs_tracker_report_stats_results();
673 Multi_fs_tracker_busy = 0;
677 // if any players have hacked info
678 for(idx=0; idx<MAX_PLAYERS; idx++){
679 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
684 // if there is only 1 player, don't store the stats
685 if((multi_num_players() <= 1) && (Multi_num_players_at_start <= 1)){
686 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);
687 multi_display_chat_msg(XSTR("<Not enough players were present at game start or end, stats will not be saved>", 1048), 0, 0);
688 multi_fs_tracker_report_stats_results();
689 Multi_fs_tracker_busy = 0;
693 // check to see if the mission is valid
694 if(multi_fs_tracker_validate_mission(Game_current_mission_filename) != MVALID_STATUS_VALID){
695 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);
696 multi_display_chat_msg(XSTR("<Server detected a non PXO validated mission. Stats will not be saved>", 1049), 0, 0);
697 Multi_fs_tracker_busy = 0;
702 // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
704 ret_val = multi_fs_store_stats_do();
705 game_set_frametime(GS_STATE_STANDALONE_POSTGAME);
707 } while(ret_val == MT_STATS_NOT_DONE);
709 // report on the results
710 multi_fs_tracker_report_stats_results();
712 // mark the module as no longer busy
713 Multi_fs_tracker_busy = 0;
718 // log freespace out of the tracker
719 void multi_fs_tracker_logout()
721 if(!Multi_fs_tracker_inited){
725 // make sure we're connected
726 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER) || !(Net_player->flags & NETINFO_FLAG_MT_CONNECTED)){
730 // otherwise, log us out
734 memset(&Multi_tracker_game_data, 0, sizeof(Multi_tracker_game_data));
735 Net_player->flags &= ~(NETINFO_FLAG_MT_CONNECTED);
738 ml_string(NOX("Server disconnecting from Game Tracker"));
741 // send a request for a list of games
742 void multi_fs_tracker_send_game_request()
744 filter_game_list_struct filter;
747 // if we're not initialized, don't do anything
748 if(!Multi_fs_tracker_inited){
752 // if we have a valid filter, use that instead
753 len = strlen(Multi_fs_tracker_filter);
754 if((len > 0) && (len < CHANNEL_LEN-1) ){
755 memset(&filter,0,sizeof(filter_game_list_struct));
757 SDL_strlcpy(filter.channel, Multi_fs_tracker_filter, SDL_arraysize(filter.channel));
758 RequestGameListWithFilter(&filter);
765 // if the API has successfully been initialized and is running
766 int multi_fs_tracker_inited()
768 return Multi_fs_tracker_inited;
771 // update our settings on the tracker regarding the current netgame stuff
772 void multi_fs_tracker_update_game(netgame_info *ng)
774 if(!Multi_fs_tracker_inited){
778 // copy in the relevant data
779 Multi_tracker_game_data.max_players = ng->max_players;
780 Multi_tracker_game_data.current_num_players = multi_num_players();
783 memset(Multi_tracker_game_data.players, 0 ,MAX_FREESPACE_PLAYERS * MAX_FREESPACE_PLAYER_NAME_LEN);
785 for(idx=0;idx<MAX_PLAYERS;idx++){
786 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
787 strcpy(Multi_tracker_game_data.players[count], Net_players[idx].player->callsign);
788 Multi_tracker_game_data.player_rank[count] = Net_players[idx].player->stats.rank;
794 SDL_strlcpy(Multi_tracker_game_data.mission_name, ng->name, SDL_arraysize(Multi_tracker_game_data.mission_name));
797 ml_string(NOX("Server updating netgame info for Game Tracker"));
800 // if we're currently busy performing some tracker operation (ie, you should wait or not)
801 int multi_fs_tracker_busy()
803 return Multi_fs_tracker_busy;
807 // -----------------------------------------------------------------------------------
808 // FREESPACE MASTER TRACKER FORWARD DEFINITIONS
811 // used with popup_till_condition() for validating freespace pilots
812 int multi_fs_validate_process()
814 // should never be here if this is not true
815 SDL_assert(Multi_fs_tracker_inited);
817 PSNET_TOP_LAYER_PROCESS();
819 // if we're still in player validation mode
820 if(Multi_validate_mode == 0){
821 switch(ValidateUser(NULL,NULL)){
822 // timeout on waiting for response
824 return MT_VALIDATE_TIMEOUT;
828 // set tracker id to -1
829 SDL_strlcpy(Multi_tracker_id_string, "-1", SDL_arraysize(Multi_tracker_id_string));
830 Multi_tracker_id = -1;
831 return MT_VALIDATE_FAIL;
835 return MT_VALIDATE_NOT_DONE;
839 // now we need to try and receive stats
841 // mark me as being valid
842 Multi_tracker_player_is_valid = 1;
844 // change the popup text
845 popup_change_text(XSTR("Attempting to get pilot stats ...",679));
847 // get my tracker id#
848 Multi_tracker_id = atoi(Multi_tracker_id_string);
849 SDL_assert(Multi_tracker_id != -1);
851 GetFSPilotData((vmt_stats_struct*)0xffffffff,NULL,NULL,0);
852 GetFSPilotData(&Multi_tracker_fs_pilot,Player->callsign,Multi_tracker_id_string,1);
855 Multi_validate_mode = 1;
856 return MT_VALIDATE_NOT_DONE;
862 switch(GetFSPilotData(NULL,NULL,NULL,0)){
865 return MT_VALIDATE_TIMEOUT;
869 return MT_VALIDATE_NOT_DONE;
873 return MT_VALIDATE_SUCCEED;
877 return MT_VALIDATE_FAIL;
881 // we're not done yet - probably should never get here
882 return MT_VALIDATE_NOT_DONE;
885 // used with popup_till_condition() for storing player stats at the end of a freespace game
886 int multi_fs_store_stats_do()
888 char tracker_id_string[512];
889 char popup_text[100];
891 SDL_assert(Multi_fs_tracker_inited);
893 PSNET_TOP_LAYER_PROCESS();
895 switch(Multi_store_stats_mode){
896 // get stats for all players
897 case MT_STORE_STATS_VALIDATE :
898 Multi_store_stats_mode = MT_STORE_STATS_GET_STATS;
901 case MT_STORE_STATS_GET_STATS:
902 // if we need to get the next player
903 if(Multi_store_stats_player_flag){
904 Multi_store_stats_player_index = multi_fs_store_stats_get_next_player(Multi_store_stats_player_index);
906 // if it returns < 0 we're done with all players and should move onto the next stage (applying mission stats)
907 if(Multi_store_stats_player_index < 0){
908 Multi_store_stats_mode = MT_STORE_STATS_ACCEPT;
909 return MT_STATS_NOT_DONE;
912 // unset this flag so we process the request
913 Multi_store_stats_player_flag = 0;
915 // fill out the information request
916 memset(tracker_id_string,0,512);
917 SDL_assert(Net_players[Multi_store_stats_player_index].tracker_player_id > 0);
919 // verify that there are no duplicate tracker id's to this one
920 multi_fs_tracker_check_dup(Net_players[Multi_store_stats_player_index].tracker_player_id,Multi_store_stats_player_index);
921 multi_fs_tracker_check_dup_callsign(&Net_players[Multi_store_stats_player_index],Multi_store_stats_player_index);
923 SDL_snprintf(tracker_id_string, SDL_arraysize(tracker_id_string), "%d", Net_players[Multi_store_stats_player_index].tracker_player_id);
924 Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = -1;
925 Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = 0;
927 // send the request itself
928 GetFSPilotData((vmt_stats_struct*)0xffffffff, NULL, NULL,0);
929 memset(&Multi_store_stats_stats, 0, sizeof(Multi_store_stats_stats));
930 if(GetFSPilotData(&Multi_store_stats_stats, Net_players[Multi_store_stats_player_index].player->callsign,tracker_id_string,1) != 0){
933 // move onto the next player
934 Multi_store_stats_player_flag = 1;
935 return MT_STATS_NOT_DONE;
938 // set the popup text
939 if(!(Game_mode & GM_STANDALONE_SERVER)){
940 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);
941 popup_change_text(popup_text);
943 return MT_STATS_NOT_DONE;
946 // process the request
947 switch(GetFSPilotData(NULL,NULL,NULL,0)){
950 // copy his stats, then flag him as done so we move onto the next guys
951 multi_stats_tracker_to_fs(&Multi_store_stats_stats,&Net_players[Multi_store_stats_player_index].player->stats);
953 // make sure we apply his mission stats now
954 scoring_do_accept(&Net_players[Multi_store_stats_player_index].player->stats);
958 // debug code to check for bogus stats
959 scoring_struct *ssp = &(Net_players[Multi_store_stats_player_index].player->stats);
960 vmt_stats_struct *vmt = &Multi_store_stats_stats;
962 if ( (ssp->missions_flown < vmt->missions_flown) || (ssp->flight_time < ssp->flight_time) || (ssp->kill_count < vmt->kill_count) ) {
968 // flag him as being completed
969 Multi_store_stats_player_flag = 1;
971 // also store this last security value so we can properly update him
972 Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = Multi_store_stats_stats.security;
973 Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = Multi_store_stats_stats.checksum;
981 case 3: case -2: case 2: case -3: case -1:
982 // this shouldn't be happening under most conditions. For debugging....
985 // flag him as done so we move onto the next guy
986 Multi_store_stats_player_flag = 1;
987 Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = -1;
988 Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = 0;
990 // mark down that the stats get for him failed
991 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_GET_FAILED;
992 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
997 // update all stats for all players locally and on client machines
998 case MT_STORE_STATS_ACCEPT :
999 // tell everyone to save their stats
1000 send_store_stats_packet(1);
1002 // reset status flags and indices
1003 Multi_store_stats_player_index = -1;
1004 Multi_store_stats_player_flag = 1;
1006 Multi_store_stats_mode = MT_STORE_STATS_SEND_STATS;
1009 // send stats to the tracker
1010 case MT_STORE_STATS_SEND_STATS:
1011 // if we need to get the next player
1012 if(Multi_store_stats_player_flag){
1013 Multi_store_stats_player_index = multi_fs_store_stats_get_next_player(Multi_store_stats_player_index);
1015 // if it returns < 0 we need to move onto the next player
1016 if(Multi_store_stats_player_index < 0){
1017 return MT_STATS_SUCCEED;
1020 Multi_store_stats_player_flag = 0;
1022 // fill in the information
1023 memset(&Multi_store_stats_stats,0,sizeof(Multi_store_stats_stats));
1025 SDL_assert(Net_players[Multi_store_stats_player_index].tracker_player_id > 0);
1027 // verify that there are no duplicate tracker id's to this one
1028 multi_fs_tracker_check_dup(Net_players[Multi_store_stats_player_index].tracker_player_id,Multi_store_stats_player_index);
1029 multi_fs_tracker_check_dup_callsign(&Net_players[Multi_store_stats_player_index],Multi_store_stats_player_index);
1030 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);
1032 Multi_store_stats_stats.security = Net_players[Multi_store_stats_player_index].s_info.tracker_security_last;
1034 // SDL_assert(Net_players[Multi_store_stats_player_index].s_info.tracker_checksum != 0);
1035 Multi_store_stats_stats.checksum = Net_players[Multi_store_stats_player_index].s_info.tracker_checksum;
1038 SendFSPilotData((vmt_stats_struct*)0xffffffff);
1039 if(SendFSPilotData(&Multi_store_stats_stats) != 0){
1042 // failed to send, try another player the next time around
1043 Multi_store_stats_player_flag = 1;
1044 return MT_STATS_NOT_DONE;
1047 // set the popup text
1048 if(!(Game_mode & GM_STANDALONE_SERVER)){
1049 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);
1050 popup_change_text(popup_text);
1053 return MT_STATS_NOT_DONE;
1056 // otherwise check on his status
1057 switch(SendFSPilotData(NULL)){
1059 case -1: case -2: case -3: case 2: case 3:
1060 // flag him as done so we move onto the next guy
1061 Multi_store_stats_player_flag = 1;
1063 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_SEND_FAILED;
1064 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1069 // flag him as done so we move onto the next guys
1070 Multi_store_stats_player_flag = 1;
1071 Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1078 // return not done yet
1079 return MT_STATS_NOT_DONE;
1082 // copy a freespace stats struct to a tracker-freespace stats struct
1083 void multi_stats_fs_to_tracker(scoring_struct *fs, vmt_stats_struct *vmt, player *pl, int tracker_id)
1085 char tracker_id_string[256];
1087 // tracker id string
1088 SDL_snprintf(tracker_id_string, SDL_arraysize(tracker_id_string),"%d", tracker_id);
1089 SDL_strlcpy(vmt->tracker_id, tracker_id_string, SDL_arraysize(vmt->tracker_id));
1092 SDL_strlcpy(vmt->pilot_name, pl->callsign, SDL_arraysize(vmt->pilot_name));
1094 // score, rank and medals
1095 vmt->score = fs->score;
1096 vmt->rank = fs->rank;
1098 SDL_assert(MAX_FS2_MEDALS == NUM_MEDALS);
1099 memcpy(vmt->medals, fs->medals, sizeof(int) * MAX_FS2_MEDALS);
1100 vmt->num_medals = MAX_FS2_MEDALS;
1102 memcpy(vmt->medals, fs->medals, sizeof(int) * MAX_FS_MEDALS);
1105 // kills and assists
1107 SDL_assert(MAX_FS2_SHIP_TYPES == MAX_SHIP_TYPES);
1108 memcpy(vmt->kills, fs->kills, sizeof(ushort) * MAX_FS2_SHIP_TYPES);
1109 vmt->num_ship_types = MAX_FS2_SHIP_TYPES;
1111 memcpy(vmt->kills, fs->kills, sizeof(int) * MAX_FS_SHIP_TYPES);
1113 vmt->assists = fs->assists;
1114 vmt->kill_count = fs->kill_count;
1115 vmt->kill_count_ok = fs->kill_count_ok;
1119 vmt->p_shots_fired = fs->p_shots_fired;
1120 vmt->s_shots_fired = fs->s_shots_fired;
1121 vmt->p_shots_hit = fs->p_shots_hit;
1122 vmt->s_shots_hit = fs->s_shots_hit;
1123 vmt->p_bonehead_hits = fs->p_bonehead_hits;
1124 vmt->s_bonehead_hits = fs->s_bonehead_hits;
1125 vmt->bonehead_kills = fs->bonehead_kills;
1127 // missions flown information
1128 vmt->missions_flown = fs->missions_flown;
1129 vmt->flight_time = fs->flight_time;
1130 vmt->last_flown = (unsigned int)fs->last_flown;
1133 // copy a tracker-freespace stats struct to a freespace stats struct
1134 void multi_stats_tracker_to_fs(vmt_stats_struct *vmt,scoring_struct *fs)
1137 int num_medals, num_ship_types;
1140 // score, rank and medals
1141 fs->score = vmt->score;
1142 fs->rank = vmt->rank;
1144 num_medals = vmt->num_medals;
1145 if(num_medals > NUM_MEDALS){
1147 num_medals = NUM_MEDALS;
1149 memset(fs->medals, 0, sizeof(int) * NUM_MEDALS);
1150 memcpy(fs->medals, vmt->medals, sizeof(int) * num_medals);
1152 SDL_zero(fs->medals);
1153 memcpy(fs->medals, vmt->medals, sizeof(int) * NUM_MEDALS);
1156 // kills and assists
1158 num_ship_types = vmt->num_ship_types;
1159 if(num_ship_types > MAX_SHIP_TYPES){
1161 num_ship_types = MAX_SHIP_TYPES;
1163 memset(fs->kills, 0, sizeof(ushort) * MAX_SHIP_TYPES);
1164 memcpy(fs->kills, vmt->kills, sizeof(ushort) * num_ship_types);
1166 SDL_zero(fs->kills);
1167 memcpy(fs->kills, vmt->kills, sizeof(int) * MAX_SHIP_TYPES);
1169 fs->assists = vmt->assists;
1170 fs->kill_count = vmt->kill_count;
1171 fs->kill_count_ok = vmt->kill_count_ok;
1174 fs->p_shots_fired = vmt->p_shots_fired;
1175 fs->s_shots_fired = vmt->s_shots_fired;
1176 fs->p_shots_hit = vmt->p_shots_hit;
1177 fs->s_shots_hit = vmt->s_shots_hit;
1178 fs->p_bonehead_hits = vmt->p_bonehead_hits;
1179 fs->s_bonehead_hits = vmt->s_bonehead_hits;
1180 fs->bonehead_kills = vmt->bonehead_kills;
1182 // missions flown information
1183 fs->missions_flown = vmt->missions_flown;
1184 fs->flight_time = vmt->flight_time;
1185 fs->last_flown = (fs_time_t)vmt->last_flown;
1186 if(fs->last_flown < 0){
1191 // process an incoming active game item
1192 void multi_fs_tracker_process_game_item(game_list *gl)
1197 for(idx=0;idx<MAX_GAME_LISTS_PER_PACKET;idx++){
1198 // skip null server addresses
1199 if(gl->game_server[idx] == 0){
1203 // package up the game information
1204 memset(&ag,0,sizeof(active_game));
1205 SDL_strlcpy(ag.name, gl->game_name[idx], SDL_arraysize(ag.name));
1206 memcpy(&ag.server_addr.addr[0], &gl->game_server[idx], IP_ADDRESS_LENGTH);
1207 ag.server_addr.type = NET_TCP;
1208 ag.server_addr.port = DEFAULT_GAME_PORT;
1210 // add to the active game list
1211 // multi_update_active_games(&ag);
1213 // query this server
1214 send_server_query(&ag.server_addr);
1218 int multi_fs_store_stats_get_next_player(int cur_player)
1222 // if we're at the end of the list
1223 if(cur_player == (MAX_PLAYERS - 1)){
1227 // find the next player
1228 for(idx=cur_player+1;idx<MAX_PLAYERS;idx++){
1230 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) ){
1235 // couldn't find one
1239 // verify that there are no duplicate tracker id's to this one
1240 void multi_fs_tracker_check_dup(int tracker_id,int player_index)
1244 // compare against all players except the passed player_index
1245 for(idx=0;idx<MAX_PLAYERS;idx++){
1246 if(idx == player_index){
1250 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].tracker_player_id != -1)){
1251 SDL_assert(Net_players[idx].tracker_player_id != tracker_id);
1256 // verify that there are no duplicate pilot callsigns
1257 void multi_fs_tracker_check_dup_callsign(net_player *player,int player_index)
1261 // compare against all players except the passed player_index
1262 for(idx=0;idx<MAX_PLAYERS;idx++){
1263 if(idx == player_index){
1267 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].tracker_player_id != -1)){
1268 SDL_assert(strcmp(player->player->callsign,Net_players[idx].player->callsign));
1273 // return an MVALID_STATUS_* constant
1274 int multi_fs_tracker_validate_mission_std()
1278 // wait for a response from the tracker
1280 ret_val = ValidateMission(NULL);
1281 } while(ret_val == 0);
1283 // report on the results
1287 std_destroy_gen_dialog();
1288 return MVALID_STATUS_UNKNOWN;
1292 std_destroy_gen_dialog();
1293 return MVALID_STATUS_INVALID;
1297 std_destroy_gen_dialog();
1298 return MVALID_STATUS_VALID;
1305 // special return values :
1309 int multi_fs_tracker_validate_mission_normal()
1311 switch(ValidateMission(NULL)){
1329 // return an MVALID_STATUS_* (see multiui.h) value, or -2 if the user has "cancelled"
1330 int multi_fs_tracker_validate_mission(char *filename)
1332 vmt_validate_mission_req_struct mission;
1333 char popup_string[512] = "";
1335 if(!Multi_fs_tracker_inited){
1336 return MVALID_STATUS_UNKNOWN;
1339 // get the checksum of the local file
1340 memset(&mission, 0, sizeof(mission));
1341 SDL_strlcpy(mission.file_name, filename, SDL_arraysize(mission.file_name));
1342 if(!cf_chksum_long(mission.file_name, (uint*)&mission.checksum)){
1343 return MVALID_STATUS_UNKNOWN;
1346 // try and validate the mission
1347 if(ValidateMission(&mission) != 0){
1348 return MVALID_STATUS_UNKNOWN;
1351 // do frames for standalone and non-standalone
1352 if(Game_mode & GM_STANDALONE_SERVER){
1355 // set the filename in the dialog
1356 std_gen_set_text(filename, 2);
1358 // validate the mission
1359 ret_code = multi_fs_tracker_validate_mission_std();
1361 // if the dialog is no longer active, cancel everything
1362 //if(!std_gen_is_active()){
1363 // return MVALID_STATUS_UNKNOWN;
1368 SDL_snprintf(popup_string, SDL_arraysize(popup_string), XSTR("Validating mission %s", 1074), filename);
1371 switch(popup_till_condition(multi_fs_tracker_validate_mission_normal, XSTR("&Cancel", 667), popup_string)){
1374 // bash some API values here so that next time we try and verify, everything works
1375 extern int MissionValidState;
1376 MissionValidState = VALID_STATE_IDLE;
1381 return MVALID_STATUS_UNKNOWN;
1385 return MVALID_STATUS_INVALID;
1389 return MVALID_STATUS_VALID;
1393 return MVALID_STATUS_UNKNOWN;
1396 // report on the results of the stats store procedure
1397 void multi_fs_tracker_report_stats_results()
1402 // tell everyone stats store is complete
1403 SDL_strlcpy(str, XSTR("<PXO stats store process complete>", 1001), SDL_arraysize(str));
1404 send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL, NULL, 1);
1405 multi_display_chat_msg(str, 0, 0);
1409 for(idx=0; idx<MAX_PLAYERS; idx++){
1410 // for all players who we care about
1411 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
1412 // if the stats get or send failed for any player, report as such
1413 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)){
1414 SDL_snprintf(str, SDL_arraysize(str), XSTR("<PXO stats store failed for player %s>", 1002), Net_players[idx].player->callsign);
1415 send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL, NULL, 1);
1417 multi_display_chat_msg(str, 0, 0);
1424 // return an MSW_STATUS_* constant
1425 int multi_fs_tracker_validate_sw_std()
1429 // wait for a response from the tracker
1431 ret_val = ValidateSquadWar(NULL, &Multi_tracker_sw_response);
1432 } while(ret_val == 0);
1434 // report on the results
1438 return MVALID_STATUS_UNKNOWN;
1442 return MVALID_STATUS_INVALID;
1446 return MVALID_STATUS_VALID;
1449 return MVALID_STATUS_UNKNOWN;
1452 // special return values :
1456 int multi_fs_tracker_validate_sw_normal()
1458 switch(ValidateSquadWar(NULL, &Multi_tracker_sw_response)){
1476 #define STUFF_SW_RESPONSE(_c, _len) do {\
1477 SDL_strlcpy(_c, "", _len);\
1480 for(_idx=0; _idx<MAX_SQUAD_RESPONSE_LEN; _idx++){\
1481 if(Multi_tracker_sw_response.reason[_idx] == '\0'){\
1487 SDL_strlcpy(_c, Multi_tracker_sw_response.reason, _len);\
1491 // return an MSW_STATUS_* value
1492 int multi_fs_tracker_validate_sw(squad_war_request *sw_req, char *bad_reply, const int max_reply_len)
1494 char popup_string[512] = "";
1496 if(!Multi_fs_tracker_inited){
1497 return MSW_STATUS_UNKNOWN;
1500 // zero the response
1501 memset(&Multi_tracker_sw_response, 0, sizeof(Multi_tracker_sw_response));
1503 // try and validate the mission
1504 if(ValidateSquadWar(sw_req, &Multi_tracker_sw_response) != 0){
1505 SDL_strlcpy(bad_reply, "Error sending request for Squad War validation", max_reply_len);
1507 return MSW_STATUS_UNKNOWN;
1510 // do frames for standalone and non-standalone
1511 if(Game_mode & GM_STANDALONE_SERVER){
1514 // validate the mission
1515 ret_code = multi_fs_tracker_validate_sw_std();
1517 // copy the return code
1518 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1522 SDL_strlcpy(popup_string, XSTR("Validating squad war", 1075), SDL_arraysize(popup_string));
1525 switch(popup_till_condition(multi_fs_tracker_validate_sw_normal, XSTR("&Cancel", 645), popup_string)){
1529 // bash some API values here so that next time we try and verify, everything works
1530 extern int SquadWarValidState;
1531 SquadWarValidState = VALID_STATE_IDLE;
1533 // copy the return code
1534 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1539 // copy the return code
1540 SDL_strlcpy(bad_reply, "Timeout", max_reply_len);
1541 return MSW_STATUS_UNKNOWN;
1545 // copy the return code
1546 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1547 return MSW_STATUS_INVALID;
1551 // copy the return code
1552 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1553 return MSW_STATUS_VALID;
1557 SDL_strlcpy(bad_reply, "Unknown error", max_reply_len);
1558 return MSW_STATUS_UNKNOWN;
1561 // popup do function
1562 // -3 Error -- Called with NULL, but no request is waiting
1563 // -2 Error -- Already sending data (hasn't timed out yet)
1564 // -1 Timeout trying to send pilot data
1566 // 1 Data succesfully sent
1567 // 2 Send Cancelled (data may still have been written already, we just haven't been ACK'd yet)
1568 // 3 Pilot not written (for some reason)
1570 int multi_fs_tracker_store_sw_do()
1572 switch(SendSWData(NULL, &Multi_tracker_sw_response)){
1590 // store the results of a squad war mission on PXO, return 1 on success
1591 int multi_fs_tracker_store_sw(squad_war_result *sw_res, char *bad_reply, const int max_reply_len)
1593 char popup_string[512] = "";
1595 // clear any old requests
1596 SendSWData((squad_war_result*)0xffffffff, NULL);
1598 // send this new request
1599 SendSWData(sw_res, &Multi_tracker_sw_response);
1602 if(Game_mode & GM_STANDALONE_SERVER){
1605 ret_code = SendSWData(NULL, &Multi_tracker_sw_response);
1606 } while(ret_code == 0);
1615 SDL_strlcpy(popup_string, XSTR("Storing SquadWar results", 1078), SDL_arraysize(popup_string));
1617 // wait for a response
1618 if(popup_till_condition(multi_fs_tracker_store_sw_do, XSTR("&Cancel", 645), popup_string) == 10){