]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_fstracker.cpp
popup() -> popup_sync() change for FS1 builds
[taylor/freespace2.git] / src / network / multi_fstracker.cpp
1 /*
2  * Copyright (C) Volition, Inc. 2005.  All rights reserved.
3  * 
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 
6  * source.
7  *
8 */
9
10 /*
11  * $Logfile: /Freespace2/code/Network/multi_fstracker.cpp $
12  * $Revision: 19 $
13  * $Date: 9/13/99 11:30a $
14  * $Author: Dave $
15  *
16  * $Log: /Freespace2/code/Network/multi_fstracker.cpp $
17  * 
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.
21  * 
22  * 18    8/30/99 5:01p Dave
23  * Made d3d do less state changing in the nebula. Use new chat server for
24  * PXO.
25  * 
26  * 17    8/25/99 4:38p Dave
27  * Updated PXO stuff. Make squad war report stuff much more nicely.
28  * 
29  * 16    8/19/99 10:59a Dave
30  * Packet loss detection.
31  * 
32  * 15    6/07/99 11:30p Dave
33  * Whoops.
34  * 
35  * 14    4/30/99 12:18p Dave
36  * Several minor bug fixes.
37  * 
38  * 13    4/09/99 2:21p Dave
39  * Multiplayer beta stuff. CD checking.
40  * 
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
44  * response packets.
45  * 
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.
49  * 
50  * 10    2/17/99 2:11p Dave
51  * First full run of squad war. All freespace and tracker side stuff
52  * works.
53  * 
54  * 9     2/12/99 6:16p Dave
55  * Pre-mission Squad War code is 95% done.
56  * 
57  * 8     2/11/99 3:08p Dave
58  * PXO refresh button. Very preliminary squad war support.
59  * 
60  * 7     2/08/99 5:07p Dave
61  * FS2 chat server support. FS2 specific validated missions.
62  * 
63  * 6     2/04/99 6:29p Dave
64  * First full working rev of FS2 PXO support.  Fixed Glide lighting
65  * problems.
66  * 
67  * 5     2/03/99 6:06p Dave
68  * Groundwork for FS2 PXO usertracker support.  Gametracker support next.
69  * 
70  * 4     12/03/98 5:22p Dave
71  * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
72  * checksumming.
73  * 
74  * 3     10/19/98 11:15a Dave
75  * Changed requirements for stats storing in PXO mode.
76  * 
77  * 2     10/07/98 10:53a Dave
78  * Initial checkin.
79  * 
80  * 1     10/07/98 10:50a Dave
81  * 
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
86  * 
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
90  * only 1 player.
91  * 
92  * 44    9/15/98 7:24p Dave
93  * Minor UI changes. Localized bunch of new text.
94  * 
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.
98  * 
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.
103  * 
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.
107  * 
108  * 40    9/04/98 3:51p Dave
109  * Put in validated mission updating and application during stats
110  * updating.
111  * 
112  * 39    9/01/98 6:48p Dave
113  * Energy suck weapon. Removed a couple of now-bogus asserts in tracker
114  * code.
115  * 
116  * 38    8/21/98 1:14p Dave
117  * Put in log system hooks in useful places.
118  * 
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
122  * 
123  * 36    7/24/98 11:14a Allender
124  * preparation for validated missions
125  * 
126  * 35    6/17/98 10:56a Dave
127  * Put in debug code for detecting potential tracker stats update
128  * problems.
129  * 
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."
133  * 
134  * 33    6/13/98 6:01p Hoffoss
135  * Externalized all new (or forgot to be added) strings to all the code.
136  * 
137  * 32    5/24/98 11:33a Dave
138  * Simplified the stats store process somewhat. Put in checks to find
139  * invalid situations.
140  * 
141  * 31    5/24/98 10:36a Dave
142  * Put in more checks/verifications for tracker stats updating.
143  * 
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
147  * 
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.
151  * 
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.
156  * 
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.
160  * 
161  * 26    5/18/98 9:15p Dave
162  * Put in network config file support.
163  * 
164  * 25    5/18/98 10:39a Dave
165  * Put in support for new tracker stats.
166  * 
167  * 24    5/14/98 12:40a Dave
168  * Still more additions to the PXO screen. Updated tracker code.
169  * 
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
173  * clients.
174  * 
175  * 22    5/08/98 7:08p Dave
176  * Lots of UI tweaking.
177  * 
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.
181  * 
182  * 20    5/05/98 3:12p Chad
183  * Process _all_ server entries in a tracker game_list struct.
184  * 
185  * 19    5/05/98 2:10p Dave
186  * Verify campaign support for testing. More new tracker code.
187  * 
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.
191  * 
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.
196  * 
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
200  * text messages.
201  * 
202  * 15    4/30/98 12:57a Dave
203  * Put in new mode for ship/weapon selection. Rearranged how game querying
204  * is done a bit.
205  * 
206  * 14    4/29/98 12:11a Dave
207  * Put in first rev of full API support for new master tracker.
208  * 
209  * 13    4/28/98 7:50p Dave
210  * Fixing a broken makefile.
211  * 
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.
216  * 
217  * 11    4/04/98 4:22p Dave
218  * First rev of UDP reliable sockets is done. Seems to work well if not
219  * overly burdened.
220  * 
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.
224  * 
225  * 9     2/10/98 8:39p Dave
226  * Fixed bugs. Made endgame sequencing more clear.
227  * 
228  * 8     2/07/98 1:51p Dave
229  * Centralized single and multiplayer stats tallying.
230  * 
231  * 7     2/05/98 7:13p Dave
232  * Added some more security to MT communications.
233  * 
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.
237  * 
238  * 5     2/03/98 8:18p Dave
239  * More MT stats transfer stuff.
240  * 
241  * 4     2/02/98 8:44p Dave
242  * Finished redoing master tracker stats transfer.
243  * 
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.
247  * 
248  * 2     1/30/98 5:53p Dave
249  * Revamped master tracker API
250  * 
251  * 1     1/30/98 5:50p Dave
252  * 
253  * $NoKeywords: $
254  */
255
256
257 #ifdef PLAT_UNIX
258 #include <netinet/in.h>
259 #endif
260
261 #include "freespace.h"
262 #include "timer.h"
263 #include "gamesequence.h"
264 #include "popup.h"
265 #include "psnet.h"
266 #include "valid.h"                                              // tracker API
267 #include "gtrack.h"                                             // tracker API
268 #include "ptrack.h"                                             // tracker API
269 #include "multi.h"
270 #include "multi_fstracker.h"
271 #include "multiutil.h"
272 #include "multiui.h"
273 #include "multimsgs.h"
274 #include "multi_log.h"
275 #include "stand_server.h"
276 #include "multi_pmsg.h"
277
278 // -----------------------------------------------------------------------------------
279 // FREESPACE MASTER TRACKER DEFINES/VARS
280 //
281
282 // if the fs tracker module has been successfully initialized
283 int Multi_fs_tracker_inited = 0;
284
285 // if we're currently performing some operation with the tracker
286 int Multi_fs_tracker_busy = 0;
287
288 // channel to associate when creating a server
289 char Multi_fs_tracker_channel[255] = "";
290
291 // channel to use when polling the tracker for games
292 char Multi_fs_tracker_filter[255] = "";
293
294
295 // -----------------------------------------------------------------------------------
296 // FREESPACE MASTER TRACKER FORWARD DECLARATIONS
297 //
298
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();
308
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
313
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
317
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
323
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
329
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);       
336
337 int Multi_tracker_player_is_valid = 0;
338 int Multi_tracker_got_response = 0;
339
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);
342
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);
345
346 // process an incoming active game item
347 void multi_fs_tracker_process_game_item(game_list *gl);
348
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);
351
352 // verify that there are no duplicate pilot callsigns
353 void multi_fs_tracker_check_dup_callsign(net_player *player,int player_index);
354
355 // report on the results of the stats store procedure
356 void multi_fs_tracker_report_stats_results();
357
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;
362
363 // -----------------------------------------------------------------------------------
364 // FREESPACE MASTER TRACKER DEFINITIONS
365 //
366
367 // give some processor time to the tracker API
368 void multi_fs_tracker_process()
369 {
370         game_list *gl;
371
372 #ifndef MAKE_FS1
373         PSNET_TOP_LAYER_PROCESS();
374 #endif
375
376         if(Multi_fs_tracker_inited){
377                 // pilot validation system
378                 ValidIdle();
379
380                 // pilot tracing system
381                 PollPTrackNet();
382
383                 // game tracking system
384                 IdleGameTracker();
385
386                 // set if we've got any pending game list items
387                 gl = GetGameList();
388                 if(gl != NULL){
389                         multi_fs_tracker_process_game_item(gl);
390                         gl = NULL;                      
391                 }
392         }
393 }
394
395 // initialize the master tracker API for Freespace
396 void multi_fs_tracker_init()
397 {       
398         // don't do anything if we're already initialized
399         if(Multi_fs_tracker_inited){
400                 return;
401         }       
402         
403         // initialize the low-level validation stuff
404         if(!InitValidateClient()){
405                 ml_printf("Error initializing tracker api (validateclient)");
406                 return;
407         }
408
409         // initialize the low-level pilot tracking stuff        
410         if(!InitPilotTrackerClient()){          
411                 ml_printf("Error initializing tracker api (pilotclient)");
412                 return;
413         }       
414
415         // intialize the low-level game tracking stuff
416 #ifndef MAKE_FS1
417         if(!InitGameTrackerClient(GT_FREESPACE2)){
418 #else
419         if(!InitGameTrackerClient(GT_FREESPACE)){
420 #endif
421                 ml_printf("Error initializing tracker api (gameclient)");
422                 return;
423         }       
424
425         nprintf(("Network","Successfully initialized tracker api\n"));
426
427         // we've successfully initialized the tracker stuff
428         Multi_fs_tracker_inited = 1;
429 }
430
431 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
432 int multi_fs_tracker_validate(int show_error)
433 {
434         validate_id_request vir;        
435
436         if(!Multi_fs_tracker_inited){
437                 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Warning, Parallax Online startup failed. Will not be able to play tracker games!",666));
438                 return 0;
439         }
440         
441         // set this to false for now
442         Multi_tracker_player_is_valid = 0;
443         Multi_tracker_got_response = 0;
444
445         // mark the module as busy
446         Multi_fs_tracker_busy = 1;              
447
448         while(1){
449                 // validate our pilot on the master tracker if possible
450                 memset(&vir,0,sizeof(vir));
451                 SDL_zero(Multi_tracker_id_string);
452                 SDL_strlcpy(vir.login, Multi_tracker_login, SDL_arraysize(vir.login));
453                 SDL_strlcpy(vir.password, Multi_tracker_passwd, SDL_arraysize(vir.password));
454                 ValidateUser(&vir,Multi_tracker_id_string);
455                 
456                 // set validation mode
457                 Multi_validate_mode = 0;
458                         
459                 int rval = popup_till_condition(multi_fs_validate_process,XSTR("&Cancel",667),XSTR("Attempting to validate pilot ...",668));
460                 switch(rval){
461                 // if we failed for one reason or another
462                 case MT_VALIDATE_FAIL :
463                         // if we're supposed to show error codes
464                         if(show_error){
465                                 popup(PF_USE_AFFIRMATIVE_ICON | PF_BODY_BIG,1,XSTR("&Ok",669),XSTR("Pilot rejected by Parallax Online!",670));
466                         }
467
468                         Multi_validate_mode = -1;
469
470                         Multi_fs_tracker_busy = 0;
471                         return 0;
472                         
473                 case MT_VALIDATE_SUCCEED :
474                         // notify the user
475                         if(Multi_tracker_fs_pilot.virgin_pilot){
476                                 multi_common_add_notify(XSTR("Successfully created and validated new pilot!",671));
477                         } else {
478                                 multi_common_add_notify(XSTR("Parallax Online pilot validation succeeded!",672));
479                         }
480
481                         // copy my statistics into my pilot file
482                         multi_stats_tracker_to_fs(&Multi_tracker_fs_pilot,&Player->stats);                      
483
484                         Multi_validate_mode = -1;
485
486                         Multi_fs_tracker_busy = 0;
487                         return 1;
488                         
489                 case MT_VALIDATE_TIMEOUT :
490                         rval = popup_sync(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_BODY_BIG,2,XSTR("&Abort",673),XSTR("&Retry",674),XSTR("Validation timed out",675));
491                         
492                         // if the user clicked abort, then leave. otherwise try again
493                         if(rval == 0){
494                                 Multi_validate_mode = -1;
495
496                                 Multi_fs_tracker_busy = 0;
497                                 return 0;
498                         }
499                         break;
500                 
501                 default : 
502                         Multi_validate_mode = -1;
503
504                         Multi_fs_tracker_busy = 0;
505                 
506                         // essentially, cancel
507                         return -1;
508                 }
509         }
510 }
511
512 // attempt to log the current game server in with the master tracker
513 void multi_fs_tracker_login_freespace()
514 {       
515         if(!Multi_fs_tracker_inited){
516                 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Warning, Parallax Online startup failed. Will not be able to play tracker games!",666));
517                 return;
518         }
519
520         // if we're already logged into a game, don't do anything
521         if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) || (Net_player->flags & NETINFO_FLAG_MT_CONNECTED) ) {
522                 return;
523         }
524
525         // pretty much all we do is make 1 call
526         memset(&Multi_tracker_game_data, 0, sizeof(Multi_tracker_game_data));
527         SDL_strlcpy(Multi_tracker_game_data.game_name, Netgame.name, SDL_arraysize(Multi_tracker_game_data.game_name));
528         Multi_tracker_game_data.type = 0;
529         Multi_tracker_game_data.state = Netgame.game_state;
530         Multi_tracker_game_data.max_players = 12;
531         Multi_tracker_game_data.current_num_players = 0;
532
533         // tricky bit to determine netgame mode / restrictions
534         if (Netgame.mode == NG_MODE_RANK_BELOW) {
535                 Multi_tracker_game_data.difficulty = -(100 + Netgame.rank_base);
536         } else if (Netgame.mode == NG_MODE_RANK_ABOVE) {
537                 Multi_tracker_game_data.difficulty = (100 + Netgame.rank_base);
538         } else {
539                 Multi_tracker_game_data.difficulty = Netgame.mode;
540         }
541
542         // if we have a valid channel string, use it            
543         if(strlen(Multi_fs_tracker_channel)){
544                 SDL_strlcpy(Multi_tracker_game_data.channel, Multi_fs_tracker_channel, SDL_arraysize(Multi_tracker_game_data.channel));
545         }       
546         
547         StartTrackerGame(&Multi_tracker_game_data);
548         Net_player->flags |= NETINFO_FLAG_MT_CONNECTED; 
549
550         // NETLOG
551         ml_string(NOX("Server connected to Game Tracker"));
552 }
553
554 // attempt to update all player statistics and scores on the tracker
555 int multi_fs_tracker_store_stats()
556 {               
557         int idx;
558
559         // NETLOG
560         ml_string(NOX("Server storing stats on User Tracker"));
561
562         // retrieve stats from tracker
563         Multi_store_stats_mode = MT_STORE_STATS_VALIDATE;
564         
565         // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
566         Multi_store_stats_player_index = -1;
567         Multi_store_stats_player_flag = 1;
568
569         // mark the module as busy
570         Multi_fs_tracker_busy = 1;
571
572         // unmark everyone's GET_FAILED flag
573         for(idx=0;idx<MAX_PLAYERS;idx++){
574                 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_GET_FAILED);
575                 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_SEND_FAILED);
576                 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_DONE);
577         }
578
579 #ifdef RELEASE_REAL
580         // if playing with an invalid ships.tbl
581         if(!Game_ships_tbl_valid){
582                 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);   
583                 multi_display_chat_msg(XSTR("<Server detected a hacked ships.tbl. Stats will not be saved>", 1044), 0, 0);
584                 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You are playing with a hacked ships.tbl, your stats will not be saved", 1045) );
585                 multi_fs_tracker_report_stats_results();
586                 Multi_fs_tracker_busy = 0;
587                 return 0;
588         }
589
590         // if playing with an invalid weapons.tbl
591         if(!Game_weapons_tbl_valid){
592                 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); 
593                 multi_display_chat_msg(XSTR("<Server detected a hacked weapons.tbl. Stats will not be saved>", 1046), 0, 0);
594                 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You are playing with a hacked weapons.tbl, your stats will not be saved", 1047) );
595                 multi_fs_tracker_report_stats_results();
596                 Multi_fs_tracker_busy = 0;
597                 return 0;
598         }
599
600         // if there is only 1 player, don't store the stats
601         if((multi_num_players() <= 1) && (Multi_num_players_at_start <= 1)){
602                 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);         
603                 multi_display_chat_msg(XSTR("<Not enough players were present at game start or end, stats will not be saved>", 1048), 0, 0);
604                 multi_fs_tracker_report_stats_results();
605                 Multi_fs_tracker_busy = 0;
606                 return 0;
607         }       
608
609         // if any players have hacked info
610         for(idx=0; idx<MAX_PLAYERS; idx++){
611                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
612                         return 0;
613                 }
614         }
615
616         // check to see if the mission is valid
617         if(multi_fs_tracker_validate_mission(Game_current_mission_filename) != MVALID_STATUS_VALID){
618                 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);  
619                 multi_display_chat_msg(XSTR("<Server detected a non PXO validated mission. Stats will not be saved>", 1049), 0, 0);
620                 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK, XSTR("This is not a PXO validated mission, your stats will not be saved", 1050));
621                 Multi_fs_tracker_busy = 0;
622                 return 0;
623         }
624 #endif
625
626         popup_till_condition(multi_fs_store_stats_do,XSTR("&Cancel",667), XSTR("Sending player stats requests ...",676));       
627
628         // send appropriate chat messages indicating stats store failure
629         multi_fs_tracker_report_stats_results();
630
631         // mark the module as not busy anymore
632         Multi_fs_tracker_busy = 0;
633
634         return 1;
635 }
636
637 // attempt to update all player statistics (standalone mode)
638 int multi_fs_std_tracker_store_stats()
639 {       
640         int ret_val;
641         int idx;
642
643         // don't do anything if this is a tracker game
644         if(!(MULTI_IS_TRACKER_GAME)){
645                 return 0;
646         }
647
648         if(!Multi_fs_tracker_inited){
649                 return 0;
650         }
651
652         // NETLOG
653         ml_string(NOX("Standalone server storing stats on User Tracker"));
654
655         // retrieve stats from tracker
656         Multi_store_stats_mode = MT_STORE_STATS_VALIDATE;
657         
658         // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
659         Multi_store_stats_player_index = -1;
660         Multi_store_stats_player_flag = 1;
661
662         // mark the module as busy
663         Multi_fs_tracker_busy = 1;
664
665         // unmark everyone's GET_FAILED flag
666         for(idx=0;idx<MAX_PLAYERS;idx++){
667                 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_GET_FAILED);
668                 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_SEND_FAILED);
669                 Net_players[idx].flags &= ~(NETINFO_FLAG_MT_DONE);
670         }
671
672 #ifdef RELEASE_REAL
673         // if playing with an invalid ships.tbl
674         if(!Game_ships_tbl_valid){      
675                 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);   
676                 multi_display_chat_msg(XSTR("<Server detected a hacked ships.tbl. Stats will not be saved>", 1044), 0, 0);
677                 multi_fs_tracker_report_stats_results();
678                 Multi_fs_tracker_busy = 0;
679                 return 0;
680         }
681
682         // if playing with an invalid weapons.tbl
683         if(!Game_weapons_tbl_valid){    
684                 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); 
685                 multi_display_chat_msg(XSTR("<Server detected a hacked weapons.tbl. Stats will not be saved>", 1046), 0, 0);
686                 multi_fs_tracker_report_stats_results();
687                 Multi_fs_tracker_busy = 0;
688                 return 0;
689         }
690
691         // if any players have hacked info
692         for(idx=0; idx<MAX_PLAYERS; idx++){
693                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
694                         return 0;
695                 }
696         }
697
698         // if there is only 1 player, don't store the stats     
699         if((multi_num_players() <= 1) && (Multi_num_players_at_start <= 1)){
700                 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);
701                 multi_display_chat_msg(XSTR("<Not enough players were present at game start or end, stats will not be saved>", 1048), 0, 0);
702                 multi_fs_tracker_report_stats_results();
703                 Multi_fs_tracker_busy = 0;
704                 return 0;
705         }
706
707         // check to see if the mission is valid 
708         if(multi_fs_tracker_validate_mission(Game_current_mission_filename) != MVALID_STATUS_VALID){            
709                 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);  
710                 multi_display_chat_msg(XSTR("<Server detected a non PXO validated mission. Stats will not be saved>", 1049), 0, 0);
711                 Multi_fs_tracker_busy = 0;
712                 return 0;
713         }
714 #endif
715         
716         // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker
717         do {            
718                 ret_val = multi_fs_store_stats_do();
719                 game_set_frametime(GS_STATE_STANDALONE_POSTGAME);
720                 multi_do_frame();
721         } while(ret_val == MT_STATS_NOT_DONE);
722
723         // report on the results                
724         multi_fs_tracker_report_stats_results();
725
726         // mark the module as no longer busy
727         Multi_fs_tracker_busy = 0;
728
729         return 1;
730 }
731
732 // log freespace out of the tracker
733 void multi_fs_tracker_logout()
734 {
735         if(!Multi_fs_tracker_inited){
736                 return;
737         }
738         
739         // make sure we're connected
740         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER) || !(Net_player->flags & NETINFO_FLAG_MT_CONNECTED)){
741                 return;
742         }
743
744         // otherwise, log us out
745         SendGameOver();
746
747         // clear our data
748         memset(&Multi_tracker_game_data, 0, sizeof(Multi_tracker_game_data));
749         Net_player->flags &= ~(NETINFO_FLAG_MT_CONNECTED);
750
751         // NETLOG
752         ml_string(NOX("Server disconnecting from Game Tracker"));
753 }
754
755 // send a request for a list of games
756 void multi_fs_tracker_send_game_request()
757 {
758         filter_game_list_struct filter;
759         int len;
760         
761         // if we're not initialized, don't do anything
762         if(!Multi_fs_tracker_inited){
763                 return;
764         }       
765
766         // if we have a valid filter, use that instead          
767         len = strlen(Multi_fs_tracker_filter);
768         if((len > 0) && (len < CHANNEL_LEN-1) ){
769                 memset(&filter,0,sizeof(filter_game_list_struct));              
770
771                 SDL_strlcpy(filter.channel, Multi_fs_tracker_filter, SDL_arraysize(filter.channel));
772                 RequestGameListWithFilter(&filter);
773         } else {        
774                 // simple API call
775                 RequestGameList();
776         }
777 }
778
779 // if the API has successfully been initialized and is running
780 int multi_fs_tracker_inited()
781 {
782         return Multi_fs_tracker_inited;
783 }
784
785 // update our settings on the tracker regarding the current netgame stuff
786 void multi_fs_tracker_update_game(netgame_info *ng)
787 {
788         if(!Multi_fs_tracker_inited){
789                 return;
790         }
791
792         if ( !(Net_player->flags & NETINFO_FLAG_MT_CONNECTED) ) {
793                 return;
794         }
795
796         SDL_assert(ng != NULL);
797
798         // copy in the relevant data
799         SDL_strlcpy(Multi_tracker_game_data.game_name, ng->name, SDL_arraysize(Multi_tracker_game_data.game_name));
800
801         Multi_tracker_game_data.state = ng->game_state;
802         Multi_tracker_game_data.max_players = ng->max_players;
803         Multi_tracker_game_data.current_num_players = multi_num_players();
804
805         // tricky bit to determine netgame mode / restrictions
806         if (Netgame.mode == NG_MODE_RANK_BELOW) {
807                 Multi_tracker_game_data.difficulty = -(100 + Netgame.rank_base);
808         } else if (Netgame.mode == NG_MODE_RANK_ABOVE) {
809                 Multi_tracker_game_data.difficulty = (100 + Netgame.rank_base);
810         } else {
811                 Multi_tracker_game_data.difficulty = Netgame.mode;
812         }
813
814 #ifdef MAKE_FS1
815         memset(Multi_tracker_game_data.players, 0 ,MAX_FREESPACE_PLAYERS * MAX_FREESPACE_PLAYER_NAME_LEN);
816         int count = 0, idx;
817         for(idx=0;idx<MAX_PLAYERS;idx++){
818                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
819                         strcpy(Multi_tracker_game_data.players[count], Net_players[idx].player->callsign);
820                         Multi_tracker_game_data.player_rank[count] = Net_players[idx].player->stats.rank;
821                         count++;
822                 }
823         }
824 #endif
825
826         SDL_strlcpy(Multi_tracker_game_data.mission_name, ng->mission_name, SDL_arraysize(Multi_tracker_game_data.mission_name));
827
828         // NETLOG
829         ml_string(NOX("Server updating netgame info for Game Tracker"));
830
831         UpdateGameData(&Multi_tracker_game_data);
832 }
833
834 // if we're currently busy performing some tracker operation (ie, you should wait or not)
835 int multi_fs_tracker_busy()
836 {
837         return Multi_fs_tracker_busy;
838 }
839
840
841 // -----------------------------------------------------------------------------------
842 // FREESPACE MASTER TRACKER FORWARD DEFINITIONS
843 //
844
845 // used with popup_till_condition() for validating freespace pilots
846 int multi_fs_validate_process()
847 {                                               
848         // should never be here if this is not true
849         SDL_assert(Multi_fs_tracker_inited);
850
851 #ifndef MAKE_FS1
852         PSNET_TOP_LAYER_PROCESS();
853 #endif
854
855         // if we're still in player validation mode
856         if(Multi_validate_mode == 0){
857                 switch(ValidateUser(NULL,NULL)){                
858                 // timeout on waiting for response
859                 case -2 :
860                         return MT_VALIDATE_TIMEOUT;                     
861
862                 // user invalid
863                 case -1:
864                         // set tracker id to -1
865                         SDL_strlcpy(Multi_tracker_id_string, "-1", SDL_arraysize(Multi_tracker_id_string));
866                         Multi_tracker_id = -1;
867                         return MT_VALIDATE_FAIL;                        
868
869                 // still waiting
870                 case 0:
871                         return MT_VALIDATE_NOT_DONE;
872         
873                 // user valid
874                 case 1:                                         
875                         // now we need to try and receive stats                 
876
877                         // mark me as being valid
878                         Multi_tracker_player_is_valid = 1;
879
880                         // change the popup text
881                         popup_change_text(XSTR("Attempting to get pilot stats ...",679));
882
883                         // get my tracker id#
884                         Multi_tracker_id = atoi(Multi_tracker_id_string);                       
885                         SDL_assert(Multi_tracker_id != -1);
886
887                         GetFSPilotData((vmt_stats_struct*)0xffffffff,NULL,NULL,0);
888                         GetFSPilotData(&Multi_tracker_fs_pilot,Player->callsign,Multi_tracker_id_string,1);
889                                 
890                         // set to mode 1
891                         Multi_validate_mode = 1;                                
892                         return MT_VALIDATE_NOT_DONE;    
893                         
894                 default :
895                         Int3();
896                 }
897         } else {                                
898                 switch(GetFSPilotData(NULL,NULL,NULL,0)){                               
899                 // timedout
900                 case -1:
901                         return MT_VALIDATE_TIMEOUT;                     
902                                 
903                 // still waiting
904                 case 0:
905                         return MT_VALIDATE_NOT_DONE;
906                         
907                 // got data
908                 case 1:
909                         return MT_VALIDATE_SUCCEED;                     
910
911                 // failure
912                 case 3:
913                         return MT_VALIDATE_FAIL;
914                 }                       
915         }
916
917         // we're not done yet - probably should never get here
918         return MT_VALIDATE_NOT_DONE;
919 }
920
921 // used with popup_till_condition() for storing player stats at the end of a freespace game
922 int multi_fs_store_stats_do()
923 {                       
924         char tracker_id_string[512];
925         char popup_text[100];
926
927         SDL_assert(Multi_fs_tracker_inited);
928
929 #ifndef MAKE_FS1
930         PSNET_TOP_LAYER_PROCESS();
931 #endif
932
933         switch(Multi_store_stats_mode){
934         // get stats for all players
935         case MT_STORE_STATS_VALIDATE : 
936                 Multi_store_stats_mode = MT_STORE_STATS_GET_STATS;
937                 break;
938
939         case MT_STORE_STATS_GET_STATS:
940                 // if we need to get the next player            
941                 if(Multi_store_stats_player_flag){
942                         Multi_store_stats_player_index = multi_fs_store_stats_get_next_player(Multi_store_stats_player_index);
943                         
944                         // if it returns < 0 we're done with all players and should move onto the next stage (applying mission stats)
945                         if(Multi_store_stats_player_index < 0){
946                                 Multi_store_stats_mode = MT_STORE_STATS_ACCEPT;
947                                 return MT_STATS_NOT_DONE;
948                         }
949
950                         // unset this flag so we process the request
951                         Multi_store_stats_player_flag = 0;
952
953                         // fill out the information request
954                         memset(tracker_id_string,0,512);
955                         SDL_assert(Net_players[Multi_store_stats_player_index].tracker_player_id > 0);
956
957                         // verify that there are no duplicate tracker id's to this one
958                         multi_fs_tracker_check_dup(Net_players[Multi_store_stats_player_index].tracker_player_id,Multi_store_stats_player_index);
959                         multi_fs_tracker_check_dup_callsign(&Net_players[Multi_store_stats_player_index],Multi_store_stats_player_index);
960
961                         SDL_snprintf(tracker_id_string, SDL_arraysize(tracker_id_string), "%d", Net_players[Multi_store_stats_player_index].tracker_player_id);
962                         Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = -1;
963                         Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = 0;
964
965                         // send the request itself
966                         GetFSPilotData((vmt_stats_struct*)0xffffffff, NULL, NULL,0);
967                         memset(&Multi_store_stats_stats, 0, sizeof(Multi_store_stats_stats));
968                         if(GetFSPilotData(&Multi_store_stats_stats, Net_players[Multi_store_stats_player_index].player->callsign,tracker_id_string,1) != 0){
969                                 Int3();
970
971                                 // move onto the next player
972                                 Multi_store_stats_player_flag = 1;
973                                 return MT_STATS_NOT_DONE;
974                         }
975
976                         // set the popup text
977                         if(!(Game_mode & GM_STANDALONE_SERVER)){
978                                 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);
979                                 popup_change_text(popup_text);
980                         }
981                         return MT_STATS_NOT_DONE;
982                 }
983
984                 // process the request
985                 switch(GetFSPilotData(NULL,NULL,NULL,0)){                                                       
986                 // got data
987                 case 1:
988                         // copy his stats, then flag him as done so we move onto the next guys
989                         multi_stats_tracker_to_fs(&Multi_store_stats_stats,&Net_players[Multi_store_stats_player_index].player->stats);
990
991                         // make sure we apply his mission stats now
992                         scoring_do_accept(&Net_players[Multi_store_stats_player_index].player->stats);
993
994 #ifndef NDEBUG
995                         {
996                                 // debug code to check for bogus stats
997                                 scoring_struct *ssp = &(Net_players[Multi_store_stats_player_index].player->stats);
998                                 vmt_stats_struct *vmt = &Multi_store_stats_stats;
999                                 
1000                                 if ( (ssp->missions_flown < vmt->missions_flown) || (ssp->flight_time < ssp->flight_time) || (ssp->kill_count < vmt->kill_count) ) {
1001                                         Int3();
1002                                 }
1003                         }
1004 #endif
1005
1006                         // flag him as being completed
1007                         Multi_store_stats_player_flag = 1;
1008
1009                         // also store this last security value so we can properly update him
1010                         Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = Multi_store_stats_stats.security;
1011                         Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = Multi_store_stats_stats.checksum;
1012                         break;
1013
1014                 // in progress
1015                 case 0:
1016                         break;
1017
1018                 // failure
1019                 case 3: case -2: case 2: case -3: case -1:
1020                         // this shouldn't be happening under most conditions. For debugging....
1021                         Int3();
1022
1023                         // flag him as done so we move onto the next guy
1024                         Multi_store_stats_player_flag = 1;
1025                         Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = -1;
1026                         Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = 0;
1027
1028                         // mark down that the stats get for him failed
1029                         Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_GET_FAILED;
1030                         Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1031                         break;                                                                          
1032                 }
1033                 break;
1034
1035         // update all stats for all players locally and on client machines
1036         case MT_STORE_STATS_ACCEPT :
1037                 // tell everyone to save their stats
1038                 send_store_stats_packet(1);
1039
1040                 // reset status flags and indices
1041                 Multi_store_stats_player_index = -1;
1042                 Multi_store_stats_player_flag = 1;
1043
1044                 Multi_store_stats_mode = MT_STORE_STATS_SEND_STATS;
1045                 break;
1046
1047         // send stats to the tracker
1048         case MT_STORE_STATS_SEND_STATS:
1049                 // if we need to get the next player            
1050                 if(Multi_store_stats_player_flag){
1051                         Multi_store_stats_player_index = multi_fs_store_stats_get_next_player(Multi_store_stats_player_index);
1052                         
1053                         // if it returns < 0 we need to move onto the next player
1054                         if(Multi_store_stats_player_index < 0){                         
1055                                 return MT_STATS_SUCCEED;
1056                         }
1057                 
1058                         Multi_store_stats_player_flag = 0;
1059
1060                         // fill in the information
1061                         memset(&Multi_store_stats_stats,0,sizeof(Multi_store_stats_stats));
1062                                 
1063                         SDL_assert(Net_players[Multi_store_stats_player_index].tracker_player_id > 0);
1064
1065                         // verify that there are no duplicate tracker id's to this one
1066                         multi_fs_tracker_check_dup(Net_players[Multi_store_stats_player_index].tracker_player_id,Multi_store_stats_player_index);
1067                         multi_fs_tracker_check_dup_callsign(&Net_players[Multi_store_stats_player_index],Multi_store_stats_player_index);
1068                         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);
1069                         
1070                         Multi_store_stats_stats.security = Net_players[Multi_store_stats_player_index].s_info.tracker_security_last;
1071
1072                         // SDL_assert(Net_players[Multi_store_stats_player_index].s_info.tracker_checksum != 0);
1073                         Multi_store_stats_stats.checksum = Net_players[Multi_store_stats_player_index].s_info.tracker_checksum;
1074                                 
1075                         // send the request
1076                         SendFSPilotData((vmt_stats_struct*)0xffffffff);
1077                         if(SendFSPilotData(&Multi_store_stats_stats) != 0){
1078                                 Int3();
1079
1080                                 // failed to send, try another player the next time around
1081                                 Multi_store_stats_player_flag = 1;
1082                                 return MT_STATS_NOT_DONE;
1083                         }
1084                         
1085                         // set the popup text
1086                         if(!(Game_mode & GM_STANDALONE_SERVER)){
1087                                 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);
1088                                 popup_change_text(popup_text);
1089                         }
1090
1091                         return MT_STATS_NOT_DONE;
1092                 }
1093                 
1094                 // otherwise check on his status                        
1095                 switch(SendFSPilotData(NULL)){                  
1096                 // error
1097                 case -1: case -2: case -3: case 2: case 3:
1098                         // flag him as done so we move onto the next guy
1099                         Multi_store_stats_player_flag = 1;                                      
1100
1101                         Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_SEND_FAILED;
1102                         Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1103                         break;
1104                         
1105                 // got data
1106                 case 1:
1107                         // flag him as done so we move onto the next guys                                       
1108                         Multi_store_stats_player_flag = 1;
1109                         Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE;
1110                         break;                  
1111                 }               
1112                 
1113                 break;
1114         }       
1115                 
1116         // return not done yet
1117         return MT_STATS_NOT_DONE;       
1118 }
1119
1120 // copy a freespace stats struct to a tracker-freespace stats struct
1121 void multi_stats_fs_to_tracker(scoring_struct *fs, vmt_stats_struct *vmt, player *pl, int tracker_id)
1122 {
1123         char tracker_id_string[256];
1124
1125         // tracker id string    
1126         SDL_snprintf(tracker_id_string, SDL_arraysize(tracker_id_string),"%d", tracker_id);
1127         SDL_strlcpy(vmt->tracker_id, tracker_id_string, SDL_arraysize(vmt->tracker_id));
1128
1129         // pilot callsign
1130         SDL_strlcpy(vmt->pilot_name, pl->callsign, SDL_arraysize(vmt->pilot_name));
1131
1132         // score, rank and medals
1133         vmt->score = fs->score;
1134         vmt->rank = fs->rank;
1135 #ifndef MAKE_FS1
1136         SDL_assert(MAX_FS2_MEDALS == NUM_MEDALS);
1137         memcpy(vmt->medals, fs->medals, sizeof(int) * MAX_FS2_MEDALS);
1138 #ifndef FS2_DEMO
1139         vmt->num_medals = MAX_FS2_MEDALS;
1140 #endif
1141 #else
1142         memcpy(vmt->medals, fs->medals, sizeof(int) * MAX_FS_MEDALS);
1143 #endif
1144
1145         // kills and assists
1146 #ifndef MAKE_FS1
1147         SDL_assert(MAX_FS2_SHIP_TYPES == MAX_SHIP_TYPES);
1148         memcpy(vmt->kills, fs->kills, sizeof(ushort) * MAX_FS2_SHIP_TYPES);
1149 #ifndef FS2_DEMO
1150         vmt->num_ship_types = MAX_FS2_SHIP_TYPES;
1151 #endif
1152 #else
1153         memcpy(vmt->kills, fs->kills, sizeof(int) * MAX_FS_SHIP_TYPES);
1154 #endif
1155         vmt->assists = fs->assists;
1156         vmt->kill_count = fs->kill_count;
1157         vmt->kill_count_ok = fs->kill_count_ok;
1158
1159
1160         // shot statistics
1161         vmt->p_shots_fired = fs->p_shots_fired;
1162         vmt->s_shots_fired = fs->s_shots_fired;
1163         vmt->p_shots_hit = fs->p_shots_hit;
1164         vmt->s_shots_hit = fs->s_shots_hit;
1165         vmt->p_bonehead_hits = fs->p_bonehead_hits;
1166         vmt->s_bonehead_hits = fs->s_bonehead_hits;
1167         vmt->bonehead_kills = fs->bonehead_kills;
1168
1169         // missions flown information
1170         vmt->missions_flown = fs->missions_flown;
1171         vmt->flight_time = fs->flight_time;
1172         vmt->last_flown = (unsigned int)fs->last_flown; 
1173 }
1174
1175 // copy a tracker-freespace stats struct to a freespace stats struct
1176 void multi_stats_tracker_to_fs(vmt_stats_struct *vmt,scoring_struct *fs)
1177 {
1178 #if !defined(MAKE_FS1) && !defined(FS2_DEMO)
1179         int num_medals, num_ship_types;
1180 #endif
1181
1182         // score, rank and medals
1183         fs->score = vmt->score;
1184         fs->rank = vmt->rank;
1185 #if !defined(MAKE_FS1) && !defined(FS2_DEMO)
1186         num_medals = vmt->num_medals;
1187         if(num_medals > NUM_MEDALS){
1188                 Int3();
1189                 num_medals = NUM_MEDALS;
1190         }
1191         memset(fs->medals, 0, sizeof(int) * NUM_MEDALS);
1192         memcpy(fs->medals, vmt->medals, sizeof(int) * num_medals);
1193 #else
1194         SDL_zero(fs->medals);
1195         memcpy(fs->medals, vmt->medals, sizeof(int) * NUM_MEDALS);
1196 #endif
1197
1198         // kills and assists
1199 #if !defined(MAKE_FS1) && !defined(FS2_DEMO)
1200         num_ship_types = vmt->num_ship_types;
1201         if(num_ship_types > MAX_SHIP_TYPES){
1202                 Int3();
1203                 num_ship_types = MAX_SHIP_TYPES;
1204         }
1205         memset(fs->kills, 0, sizeof(ushort) * MAX_SHIP_TYPES);
1206         memcpy(fs->kills, vmt->kills, sizeof(ushort) * num_ship_types);
1207 #else
1208         SDL_zero(fs->kills);
1209         memcpy(fs->kills, vmt->kills, sizeof(int) * MAX_SHIP_TYPES);
1210 #endif
1211         fs->assists = vmt->assists;
1212         fs->kill_count = vmt->kill_count;
1213         fs->kill_count_ok = vmt->kill_count_ok;
1214
1215         // shot statistics
1216         fs->p_shots_fired = vmt->p_shots_fired;
1217         fs->s_shots_fired = vmt->s_shots_fired;
1218         fs->p_shots_hit = vmt->p_shots_hit;
1219         fs->s_shots_hit = vmt->s_shots_hit;
1220         fs->p_bonehead_hits = vmt->p_bonehead_hits;
1221         fs->s_bonehead_hits = vmt->s_bonehead_hits;
1222         fs->bonehead_kills = vmt->bonehead_kills;
1223
1224         // missions flown information
1225         fs->missions_flown = vmt->missions_flown;
1226         fs->flight_time = vmt->flight_time;
1227         fs->last_flown = (fs_time_t)vmt->last_flown;
1228         if(fs->last_flown < 0){
1229                 fs->last_flown = 0;
1230         }
1231 }
1232
1233 // process an incoming active game item
1234 void multi_fs_tracker_process_game_item(game_list *gl)
1235 {
1236         active_game ag; 
1237         int idx;
1238
1239         for(idx=0;idx<MAX_GAME_LISTS_PER_PACKET;idx++){
1240                 // skip null server addresses
1241                 if(gl->game_server[idx] == 0){
1242                         continue;
1243                 }
1244
1245                 // package up the game information
1246                 memset(&ag,0,sizeof(active_game));
1247                 SDL_strlcpy(ag.name, gl->game_name[idx], SDL_arraysize(ag.name));
1248                 memcpy(&ag.server_addr.addr[0], &gl->game_server[idx], IP_ADDRESS_LENGTH);
1249                 ag.server_addr.type = NET_TCP;
1250                 ag.server_addr.port = ntohs(gl->port[idx]); //DEFAULT_GAME_PORT;
1251
1252                 // add to the active game list
1253                 // multi_update_active_games(&ag);
1254
1255                 // query this server
1256                 send_server_query(&ag.server_addr);
1257         }
1258 }
1259
1260 int multi_fs_store_stats_get_next_player(int cur_player)
1261 {
1262         int idx;
1263
1264         // if we're at the end of the list
1265         if(cur_player == (MAX_PLAYERS - 1)){
1266                 return -2;
1267         }
1268
1269         // find the next player
1270         for(idx=cur_player+1;idx<MAX_PLAYERS;idx++){
1271                 // STANDALONE_ONLY
1272                 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) ){
1273                         return idx;
1274                 }
1275         }
1276
1277         // couldn't find one
1278         return -2;
1279 }
1280
1281 // verify that there are no duplicate tracker id's to this one
1282 void multi_fs_tracker_check_dup(int tracker_id,int player_index)
1283 {
1284         int idx;
1285
1286         // compare against all players except the passed player_index   
1287         for(idx=0;idx<MAX_PLAYERS;idx++){
1288                 if(idx == player_index){
1289                         continue;
1290                 }
1291                 
1292                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].tracker_player_id != -1)){
1293                         SDL_assert(Net_players[idx].tracker_player_id != tracker_id);
1294                 }
1295         }
1296 }
1297
1298 // verify that there are no duplicate pilot callsigns
1299 void multi_fs_tracker_check_dup_callsign(net_player *player,int player_index)
1300 {
1301         int idx;
1302
1303         // compare against all players except the passed player_index   
1304         for(idx=0;idx<MAX_PLAYERS;idx++){
1305                 if(idx == player_index){
1306                         continue;
1307                 }
1308                 
1309                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].tracker_player_id != -1)){
1310                         SDL_assert(strcmp(player->player->callsign,Net_players[idx].player->callsign));
1311                 }
1312         }
1313 }
1314
1315 // return an MVALID_STATUS_* constant
1316 int multi_fs_tracker_validate_mission_std()
1317 {
1318         int ret_val;
1319         
1320         // wait for a response from the tracker
1321         do {            
1322                 ret_val = ValidateMission(NULL);                                
1323         } while(ret_val == 0);
1324
1325         // report on the results
1326         switch(ret_val){
1327         // timeout
1328         case -2:
1329                 // consider timeout to be fatal and cancel validation
1330                 return -2;      //MVALID_STATUS_UNKNOWN;
1331
1332         // invalid
1333         case -1:
1334                 return MVALID_STATUS_INVALID;
1335
1336         // valid, success
1337         case 1:
1338                 return MVALID_STATUS_VALID;
1339         }
1340
1341         Int3();
1342         return 0;
1343 }
1344
1345 // special return values :
1346 // 1 for timeout
1347 // 2 for invalid
1348 // 3 for valid
1349 int multi_fs_tracker_validate_mission_normal()
1350 {       
1351         switch(ValidateMission(NULL)){
1352         // timeout
1353         case -2:
1354                 return 1;
1355
1356         // invalid
1357         case -1 :
1358                 return 2;
1359
1360         // valid
1361         case 1:
1362                 return 3;
1363         }
1364
1365         // not done yet
1366         return 0;
1367 }       
1368
1369 // return an MVALID_STATUS_* (see multiui.h) value, or -2 if the user has "cancelled"
1370 int multi_fs_tracker_validate_mission(char *filename)
1371 {       
1372         vmt_validate_mission_req_struct mission;        
1373         char popup_string[512] = "";
1374
1375         if(!Multi_fs_tracker_inited){
1376                 return MVALID_STATUS_UNKNOWN;
1377         }
1378         
1379         // get the checksum of the local file   
1380         memset(&mission, 0, sizeof(mission));
1381         SDL_strlcpy(mission.file_name, filename, SDL_arraysize(mission.file_name));
1382         if(!cf_chksum_long(mission.file_name, (uint*)&mission.checksum)){
1383                 return MVALID_STATUS_UNKNOWN;
1384         }       
1385
1386         // try and validate the mission
1387         if(ValidateMission(&mission) != 0){
1388                 return MVALID_STATUS_UNKNOWN;
1389         }
1390
1391         // do frames for standalone and non-standalone
1392         if(Game_mode & GM_STANDALONE_SERVER){           
1393                 int ret_code;
1394
1395                 // set the filename in the dialog
1396                 std_gen_set_text(filename, 2);
1397
1398                 // validate the mission
1399                 ret_code = multi_fs_tracker_validate_mission_std();
1400
1401                 // if the dialog is no longer active, cancel everything
1402                 //if(!std_gen_is_active()){
1403                 //      return MVALID_STATUS_UNKNOWN;
1404                 //}             
1405
1406                 return ret_code;
1407         } else {
1408                 SDL_snprintf(popup_string, SDL_arraysize(popup_string), XSTR("Validating mission %s", 1074), filename);
1409
1410                 // run a popup
1411                 switch(popup_till_condition(multi_fs_tracker_validate_mission_normal, XSTR("&Cancel", 667), popup_string)){
1412                 // cancel 
1413                 case 0: 
1414                         // bash some API values here so that next time we try and verify, everything works
1415                         extern int MissionValidState;
1416                         MissionValidState = VALID_STATE_IDLE;
1417                         return -2;
1418
1419                 // timeout
1420                 case 1:
1421                         return MVALID_STATUS_UNKNOWN;
1422
1423                 // invalid
1424                 case 2:
1425                         return MVALID_STATUS_INVALID;
1426
1427                 // valid
1428                 case 3:
1429                         return MVALID_STATUS_VALID;
1430                 }
1431         }
1432
1433         return MVALID_STATUS_UNKNOWN;
1434 }
1435
1436 // report on the results of the stats store procedure
1437 void multi_fs_tracker_report_stats_results()
1438 {
1439         int idx;
1440         char str[512] = "";
1441
1442         // tell everyone stats store is complete
1443         SDL_strlcpy(str, XSTR("<PXO stats store process complete>", 1001), SDL_arraysize(str));
1444         send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL, NULL, 1);   
1445         multi_display_chat_msg(str, 0, 0);      
1446         ml_string(str);
1447
1448         // for all players
1449         for(idx=0; idx<MAX_PLAYERS; idx++){             
1450                 // for all players who we care about
1451                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
1452                         // if the stats get or send failed for any player, report as such
1453                         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)){                                    
1454                                 SDL_snprintf(str, SDL_arraysize(str), XSTR("<PXO stats store failed for player %s>", 1002), Net_players[idx].player->callsign);
1455                                 send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL, NULL, 1);
1456                                 
1457                                 multi_display_chat_msg(str, 0, 0);
1458                                 ml_string(str);
1459                         }
1460                 }
1461         }
1462 }
1463
1464 // return an MSW_STATUS_* constant
1465 int multi_fs_tracker_validate_sw_std()
1466 {
1467         int ret_val;
1468         
1469         // wait for a response from the tracker
1470         do {            
1471                 ret_val = ValidateSquadWar(NULL, &Multi_tracker_sw_response);
1472         } while(ret_val == 0);
1473
1474         // report on the results
1475         switch(ret_val){
1476         // timeout
1477         case -2:                
1478                 return MVALID_STATUS_UNKNOWN;
1479
1480         // invalid
1481         case -1:                
1482                 return MVALID_STATUS_INVALID;
1483
1484         // valid, success
1485         case 1:         
1486                 return MVALID_STATUS_VALID;
1487         }
1488         
1489         return MVALID_STATUS_UNKNOWN;
1490 }
1491
1492 // special return values :
1493 // 1 for timeout
1494 // 2 for invalid
1495 // 3 for valid
1496 int multi_fs_tracker_validate_sw_normal()
1497 {       
1498         switch(ValidateSquadWar(NULL, &Multi_tracker_sw_response)){
1499         // timeout
1500         case -2:
1501                 return 1;
1502
1503         // invalid
1504         case -1 :
1505                 return 2;
1506
1507         // valid
1508         case 1:
1509                 return 3;
1510         }
1511
1512         // not done yet
1513         return 0;
1514 }       
1515
1516 #define STUFF_SW_RESPONSE(_c, _len) do {\
1517         SDL_strlcpy(_c, "", _len);\
1518         int _idx;\
1519         int _bogus = 1;\
1520         for(_idx=0; _idx<MAX_SQUAD_RESPONSE_LEN; _idx++){\
1521                 if(Multi_tracker_sw_response.reason[_idx] == '\0'){\
1522                         _bogus = 0;\
1523                         break;\
1524                 }\
1525         }\
1526         if(!_bogus){\
1527                 SDL_strlcpy(_c, Multi_tracker_sw_response.reason, _len);\
1528         }\
1529 } while(0);
1530
1531 // return an MSW_STATUS_* value
1532 int multi_fs_tracker_validate_sw(squad_war_request *sw_req, char *bad_reply, const int max_reply_len)
1533 {
1534         char popup_string[512] = "";
1535
1536         if(!Multi_fs_tracker_inited){
1537                 return MSW_STATUS_UNKNOWN;
1538         }       
1539
1540         // zero the response
1541         memset(&Multi_tracker_sw_response, 0, sizeof(Multi_tracker_sw_response));
1542         
1543         // try and validate the mission
1544         if(ValidateSquadWar(sw_req, &Multi_tracker_sw_response) != 0){
1545                 SDL_strlcpy(bad_reply, "Error sending request for Squad War validation", max_reply_len);
1546
1547                 return MSW_STATUS_UNKNOWN;
1548         }
1549
1550         // do frames for standalone and non-standalone
1551         if(Game_mode & GM_STANDALONE_SERVER){           
1552                 int ret_code;           
1553
1554                 // validate the mission
1555                 ret_code = multi_fs_tracker_validate_sw_std();          
1556
1557                 // copy the return code
1558                 STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1559
1560                 return ret_code;
1561         } else {
1562                 SDL_strlcpy(popup_string, XSTR("Validating squad war", 1075), SDL_arraysize(popup_string));
1563
1564                 // run a popup
1565                 switch(popup_till_condition(multi_fs_tracker_validate_sw_normal, XSTR("&Cancel", 645), popup_string)){
1566                 // cancel 
1567                 case -1:
1568                 case 0: 
1569                         // bash some API values here so that next time we try and verify, everything works
1570                         extern int SquadWarValidState;
1571                         SquadWarValidState = VALID_STATE_IDLE;
1572
1573                         // copy the return code
1574                         STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1575                         return -2;
1576
1577                 // timeout
1578                 case 1:
1579                         // copy the return code                 
1580                         SDL_strlcpy(bad_reply, "Timeout", max_reply_len);
1581                         return MSW_STATUS_UNKNOWN;
1582
1583                 // invalid
1584                 case 2:
1585                         // copy the return code
1586                         STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1587                         return MSW_STATUS_INVALID;
1588
1589                 // valid
1590                 case 3:
1591                         // copy the return code
1592                         STUFF_SW_RESPONSE(bad_reply, max_reply_len);
1593                         return MSW_STATUS_VALID;
1594                 }
1595         }
1596
1597         SDL_strlcpy(bad_reply, "Unknown error", max_reply_len);
1598         return MSW_STATUS_UNKNOWN;
1599 }
1600
1601 // popup do function
1602 // -3   Error -- Called with NULL, but no request is waiting
1603 // -2   Error -- Already sending data (hasn't timed out yet)
1604 // -1   Timeout trying to send pilot data
1605 // 0    Sending
1606 // 1    Data succesfully sent
1607 // 2    Send Cancelled (data may still have been written already, we just haven't been ACK'd yet)
1608 // 3    Pilot not written (for some reason)
1609   
1610 int multi_fs_tracker_store_sw_do()
1611 {
1612         switch(SendSWData(NULL, &Multi_tracker_sw_response)){
1613         // failure
1614         case -3:
1615         case -2:
1616         case -1:
1617         case 2:
1618         case 3:
1619                 return 1;
1620
1621         // success
1622         case 1:
1623                 return 10;
1624         }
1625
1626         // not done
1627         return 0;
1628 }
1629
1630 // store the results of a squad war mission on PXO, return 1 on success
1631 int multi_fs_tracker_store_sw(squad_war_result *sw_res, char *bad_reply, const int max_reply_len)
1632 {
1633         char popup_string[512] = "";
1634
1635         // clear any old requests
1636         SendSWData((squad_war_result*)0xffffffff, NULL);
1637
1638         // send this new request
1639         SendSWData(sw_res, &Multi_tracker_sw_response);
1640
1641         // standalone
1642         if(Game_mode & GM_STANDALONE_SERVER){
1643                 int ret_code;
1644                 do {
1645                         ret_code = SendSWData(NULL, &Multi_tracker_sw_response);
1646                 } while(ret_code == 0);
1647
1648                 // success
1649                 if(ret_code == 1){
1650                         return 1;
1651                 }
1652         }
1653         // non-standalone
1654         else {
1655                 SDL_strlcpy(popup_string, XSTR("Storing SquadWar results", 1078), SDL_arraysize(popup_string));
1656
1657                 // wait for a response
1658                 if(popup_till_condition(multi_fs_tracker_store_sw_do, XSTR("&Cancel", 645), popup_string) == 10){
1659                         // success
1660                         return 1;
1661                 }
1662         }
1663
1664         // failure
1665         return 0;
1666 }
1667