]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_endgame.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / network / multi_endgame.cpp
1 /*
2  * $Logfile: /Freespace2/code/Network/multi_endgame.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * $Log$
8  * Revision 1.2  2002/05/07 03:16:47  theoddone33
9  * The Great Newline Fix
10  *
11  * Revision 1.1.1.1  2002/05/03 03:28:10  root
12  * Initial import.
13  * 
14  * 
15  * 10    8/22/99 1:55p Dave
16  * Cleaned up host/team-captain leaving code.
17  * 
18  * 9     8/22/99 1:19p Dave
19  * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
20  * which d3d cards are detected.
21  * 
22  * 8     4/08/99 1:05p Dave
23  * Fixed some leave game packet problems. Updated builtin mission list for
24  * multiplayer.
25  * 
26  * 7     3/24/99 4:05p Dave
27  * Put in support for assigning the player to a specific squadron with a
28  * specific logo. Preliminary work for doing pos/orient checksumming in
29  * multiplayer to reduce bandwidth.
30  * 
31  * 6     11/19/98 4:57p Dave
32  * Ignore PXO option if IPX is selected.
33  * 
34  * 5     11/19/98 4:19p Dave
35  * Put IPX sockets back in psnet. Consolidated all multiplayer config
36  * files into one.
37  * 
38  * 4     11/19/98 8:03a Dave
39  * Full support for D3-style reliable sockets. Revamped packet lag/loss
40  * system, made it receiver side and at the lowest possible level.
41  * 
42  * 3     11/05/98 5:55p Dave
43  * Big pass at reducing #includes
44  * 
45  * 2     10/07/98 10:53a Dave
46  * Initial checkin.
47  * 
48  * 1     10/07/98 10:50a Dave
49  * 
50  * 36    9/17/98 3:08p Dave
51  * PXO to non-pxo game warning popup. Player icon stuff in create and join
52  * game screens. Upped server count refresh time in PXO to 35 secs (from
53  * 20).
54  * 
55  * 35    9/15/98 7:24p Dave
56  * Minor UI changes. Localized bunch of new text.
57  * 
58  * 34    9/14/98 3:40p Allender
59  * better error checking for invalid number of waves for player wings in a
60  * multiplayer game.  Better popup message in FreeSpace side.
61  * 
62  * 33    9/11/98 5:53p Dave
63  * Final revisions to kick system changes.
64  * 
65  * 32    9/11/98 5:08p Dave
66  * More tweaks to kick notification system.
67  * 
68  * 31    9/11/98 4:14p Dave
69  * Fixed file checksumming of < file_size. Put in more verbose kicking and
70  * PXO stats store reporting.
71  * 
72  * 30    8/07/98 10:15a Allender
73  * changed the way the endgame sequencing timer works so that timer wrap
74  * doesn't hurt.  Also use the obj_set_flags for the COULD_BE_PLAYER flag
75  * 
76  * 29    7/24/98 9:27a Dave
77  * Tidied up endgame sequencing by removing several old flags and
78  * standardizing _all_ endgame stuff with a single function call.
79  * 
80  * 28    7/13/98 5:34p Lawrance
81  * index a localized string in multi_endgame.cpp
82  * 
83  * 27    7/13/98 5:19p Dave
84  * 
85  * 26    6/30/98 4:53p Allender
86  * fixed endgame problems where standalone wasn't properly dropping
87  * everyone out of a game.  Be sure that timestamp for endgame processing
88  * gets reset
89  * 
90  * 25    6/13/98 9:32p Mike
91  * Kill last character in file which caused "Find in Files" to report the
92  * file as "not a text file."
93  * 
94  * 24    6/13/98 6:01p Hoffoss
95  * Externalized all new (or forgot to be added) strings to all the code.
96  * 
97  * 23    6/13/98 3:18p Hoffoss
98  * NOX()ed out a bunch of strings that shouldn't be translated.
99  * 
100  * 22    5/24/98 8:15p Dave
101  * Tweaked pxo some more. 
102  * 
103  * 21    5/21/98 10:09a Dave
104  * Enable DNS checking for PXO and both trackers.
105  * Fixed problem with leaving the mission from the pause state. Fixed
106  * build errors in multimsgs.cpp
107  * 
108  * 20    5/20/98 9:01p Allender
109  * change RELEASE to NDEBUG.  Fix reentryancy problem in process_endgame
110  * for multiplayer
111  * 
112  * 19    5/17/98 11:54p Allender
113  * only clear flying controls when in mission
114  * 
115  * 18    5/17/98 11:34p Allender
116  * deal with resetting player controls better
117  * 
118  * 17    5/17/98 6:32p Dave
119  * Make sure clients/servers aren't kicked out of the debriefing when team
120  * captains leave a game. Fixed chatbox off-by-one error. Fixed image
121  * xfer/pilot info popup stuff.
122  * 
123  * 16    5/15/98 5:15p Dave
124  * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy
125  * status for team vs. team. Put in asserts to check for invalid team vs.
126  * team situations.
127  * 
128  * 15    5/15/98 12:09a Dave
129  * New tracker api code. New game tracker code. Finished up first run of
130  * the PXO screen. Fixed a few game server list exceptions.
131  * 
132  * 14    5/14/98 12:40a Dave
133  * Still more additions to the PXO screen. Updated tracker code.
134  * 
135  * 13    5/09/98 7:16p Dave
136  * Put in CD checking. Put in standalone host password. Made pilot into
137  * popup scrollable.
138  * 
139  * 12    5/08/98 5:05p Dave
140  * Go to the join game screen when quitting multiplayer. Fixed mission
141  * text chat bugs. Put mission type symbols on the create game list.
142  * Started updating standalone gui controls.
143  * 
144  * 11    5/07/98 6:26p Dave
145  * Fix strange boundary conditions which arise when players die/respawn
146  * while the game is being ended. Spiff up the chatbox doskey thing a bit.
147  * 
148  * 10    5/07/98 3:29p Jim
149  * Make sure standalone doesn't display a popup when ending a game.
150  * 
151  * 9     5/05/98 5:02p Dave
152  * Fix end-of-campaign sequencing to work right. Make all individual
153  * missions of a campaign replayable.
154  * 
155  * 8     5/04/98 10:39p Dave
156  * Put in endgame sequencing.  Need to check campaign situations.
157  * Realigned ship info on team select screen.
158  * 
159  * 7     5/04/98 1:43p Dave
160  * Fixed up a standalone resetting problem. Fixed multiplayer stats
161  * collection for clients. Make sure all multiplayer ui screens have the
162  * correct palette at all times.
163  * 
164  * 6     5/03/98 7:04p Dave
165  * Make team vs. team work mores smoothly with standalone. Change how host
166  * interacts with standalone for picking missions. Put in a time limit for
167  * ingame join ship select. Fix ingame join ship select screen for Vasudan
168  * ship icons.
169  * 
170  * 5     4/30/98 5:12p Dave
171  * Fixed game polling code for joining clients. Reworked some file xfer
172  * stuff.
173  * 
174  * 4     4/30/98 12:13a Allender
175  * reset control info and afterburner when entering the quit game code.
176  * Prevents odd things from happening while the popup is up.
177  * 
178  * 3     4/28/98 5:10p Dave
179  * Fixed multi_quit_game() client side sequencing problem. Turn off
180  * afterburners when ending multiplayer mission. Begin integration of mt
181  * API from Kevin Bentley.
182  * 
183  * 2     4/23/98 1:28a Dave
184  * Seemingly nailed the current_primary_bank and current_secondary_bank -1
185  * problem. Made sure non-critical button presses are _never_ sent to the
186  * server.
187  * 
188  * 1     4/22/98 5:50p Dave
189  *  
190  * 
191  * $NoKeywords: $
192  */
193
194 // #include <windows.h>
195 #include "multi.h"
196 #include "popup.h"
197 #include "object.h"
198 #include "freespace.h"
199 #include "gamesequence.h"
200 #include "chatbox.h"
201 #include "popupdead.h"
202 #include "multi_endgame.h"
203 #include "multimsgs.h"
204 #include "multiui.h"
205 #include "multiutil.h"
206 #include "multi_pmsg.h"
207
208
209 // ----------------------------------------------------------------------------------------------------------
210 // Put all functions/data related to leaving a netgame, handling players leaving, handling the server leaving,
211 // and notifying the user of all of these actions, here.
212 //
213
214
215 // ----------------------------------------------------------------------------------------------------------
216 // MULTI ENDGAME DEFINES/VARS
217 //
218
219
220 // set when the server/client has ended the game on some notification or error and is waiting for clients to leave
221 #define MULTI_ENDGAME_SERVER_WAIT                               5.0f
222 int Multi_endgame_server_waiting = 0;
223 float Multi_endgame_server_wait_stamp = -1.0f;
224 int Multi_endgame_client_waiting = 0;
225
226 // error/notification codes (taken from parameters to multi_quit_game(...)
227 int Multi_endgame_notify_code;
228 int Multi_endgame_error_code;
229 int Multi_endgame_wsa_error;
230
231 // for reentrancy problems on standalone
232 int Multi_endgame_processing;
233
234 // ----------------------------------------------------------------------------------------------------------
235 // MULTI ENDGAME FORWARD DECLARATIONS
236 //
237
238 // called when a given netgame is about to end completely
239 void multi_endgame_cleanup();
240
241 // throw up a popup with the given notification code and optional winsock code
242 void multi_endgame_popup(int notify_code,int error_code,int wsa_error = -1);
243
244 // called when server is waiting for clients to disconnect
245 int multi_endgame_server_ok_to_leave();
246
247 // check to see if we need to be warping out (strange transition possibilities)
248 void multi_endgame_check_for_warpout();
249
250
251 // ----------------------------------------------------------------------------------------------------------
252 // MULTI ENDGAME FUNCTIONS
253 //
254
255 // initialize the endgame processor (call when joining/starting a new netgame)
256 void multi_endgame_init()
257 {
258         // set this so that the server/client knows he hasn't tried to end the game
259         Multi_endgame_server_waiting = 0;       
260         Multi_endgame_client_waiting = 0;
261
262         // reset the timestamp used when server is waiting for all other clients to leave.
263         Multi_endgame_server_wait_stamp = -1.0f;
264
265         // initialiaze all endgame notify and error codes
266         Multi_endgame_notify_code = -1;
267         Multi_endgame_error_code = -1;
268         Multi_endgame_wsa_error = -1;
269
270         // for reentrancy problems in to endgame_process
271         Multi_endgame_processing = 0;
272 }
273
274 // process all endgame related events
275 void multi_endgame_process()
276 {
277         if ( Multi_endgame_processing )
278                 return;
279
280         Multi_endgame_processing = 1;
281
282         // check to see if we need to be warping out (strange transition possibilities)
283         multi_endgame_check_for_warpout();
284         
285         // if we're the server of the game
286         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
287                 // if we're not waiting for clients to leave, do nothing
288                 if(!Multi_endgame_server_waiting){
289                         Multi_endgame_processing = 0;
290                         return;
291                 }
292
293                 // if a popup is already active, do nothing             
294                 if(popup_active()){
295                         Multi_endgame_processing = 0;
296                         return;
297                 }
298
299                 // otherwise popup until things are hunky-dory
300                 if(!multi_endgame_server_ok_to_leave()){
301                         if(Game_mode & GM_STANDALONE_SERVER){
302                                 while(!multi_endgame_server_ok_to_leave()){
303                                         // run networking, etc.
304                                         game_set_frametime(-1);
305                                         game_do_state_common(gameseq_get_state());
306                                 }
307                         } else {
308                                 popup_till_condition( multi_endgame_server_ok_to_leave , XSTR("&Cancel",645), XSTR("Waiting for clients to disconnect",646));           
309                         }
310                 }
311
312                 // mark myself as not waiting and get the hell out              
313                 multi_endgame_cleanup();        
314         } else {
315                 // if we're not waiting to leave the game, do nothing
316                 if(!Multi_endgame_client_waiting){
317                         Multi_endgame_processing = 0;
318                         return;
319                 }
320
321                 // otherwise, check to see if there is a popup active
322                 if(popup_active()){
323                         Multi_endgame_processing = 0;
324                         return;
325                 }
326
327                 // if not, then we are good to leave            
328                 multi_endgame_cleanup();
329         }
330
331         Multi_endgame_processing = 0;
332 }
333
334 // if the game has been flagged as ended (ie, its going to be reset)
335 int multi_endgame_ending()
336 {
337         return (Multi_endgame_client_waiting || Multi_endgame_server_waiting);
338 }
339
340 // reentrancy check
341 int Multi_quit_game = 0;
342 // general quit function, with optional notification, error, and winsock error codes
343 int multi_quit_game(int prompt, int notify_code, int err_code, int wsa_error)
344 {
345         int ret_val,quit_already;
346
347         // check for reentrancy
348         if(Multi_quit_game){
349                 return 0;
350         }
351
352         // if we're not connected or have not net-player
353         if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_CONNECTED)){
354                 return 1;
355         }
356
357         // reentrancy
358         Multi_quit_game = 1;
359
360         quit_already = 0;
361
362         // reset my control info so that I don't continually do whacky stuff.  This is ugly
363         //player_control_reset_ci( &Player->ci );
364         if ( Game_mode & GM_IN_MISSION ) {
365                 memset(&Player->ci, 0, sizeof(Player->ci) );
366                 Player->ci.afterburner_stop = 1;
367                 physics_read_flying_controls( &Player_obj->orient, &Player_obj->phys_info, &(Player->ci), flFrametime);
368         }
369
370         // CASE 1 - response to a user request
371         // if there is no associated notification or error code, don't override the prompt argument
372         if((err_code == -1) && (notify_code == -1)){
373                 // if we're the server and we're already waiting for clients to leave, don't do anything
374                 if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && Multi_endgame_server_waiting){
375                         Multi_quit_game = 0;
376                         return 0;
377                 }
378
379                 // if we're the client and we're already waiting to leave, don't do anythin
380                 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER) && Multi_endgame_client_waiting){
381                         Multi_quit_game = 0;
382                         return 0;
383                 }
384
385                 // see if we should be prompting the host for confirmation
386                 if((prompt==PROMPT_HOST || prompt==PROMPT_ALL) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
387                         int p_flags;
388
389                         p_flags = PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_BODY_BIG;
390                         if ( Game_mode & GM_IN_MISSION )
391                                 p_flags |= PF_RUN_STATE;
392
393                         ret_val = popup(p_flags,2,POPUP_CANCEL,POPUP_OK,XSTR("Warning - quitting will end the game for all players!",647));
394
395                         // check for host cancel
396                         if((ret_val == 0) || (ret_val == -1)){
397                                 Multi_quit_game = 0;
398                                 return 0;
399                         }
400
401                         // set this so that under certain circumstances, we don't call the popup below us as well
402                         quit_already = 1;
403                 }
404
405                 // see if we should be prompting the client for confirmation
406                 if((prompt==PROMPT_CLIENT || prompt==PROMPT_ALL) && !quit_already){
407                         ret_val = popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_BODY_BIG,2,POPUP_NO,POPUP_YES,XSTR("Are you sure you want to quit?",648));
408
409                         // check for host cancel
410                         if((ret_val == 0) || (ret_val == -1)){
411                                 Multi_quit_game = 0;
412                                 return 0;
413                         }
414                         quit_already = 1;
415                 }
416
417                 // if i'm the server of the game, tell all clients that i'm leaving, then wait
418                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
419                         send_netgame_end_error_packet(MULTI_END_NOTIFY_SERVER_LEFT,MULTI_END_ERROR_NONE);
420
421                         // set the waiting flag and the waiting timestamp
422                         Multi_endgame_server_waiting = 1;
423                         Multi_endgame_server_wait_stamp = MULTI_ENDGAME_SERVER_WAIT;
424                 }
425                 // if i'm the client, quit now
426                 else {
427                         multi_endgame_cleanup();
428                 }
429         }
430         // CASE 2 - response to an error code or packet from the server
431         // this is the case where we're being forced to quit the game because of some error or other notification
432         else {                          
433                 // if i'm the server, send a packet to the clients telling them that I'm leaving and why
434                 if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && !Multi_endgame_server_waiting){
435                         // if we're in the debrief state, mark down that the server has left the game
436                         if(((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) && !(Game_mode & GM_STANDALONE_SERVER)){
437                                 multi_debrief_server_left();
438
439                                 // add a message to the chatbox
440                                 multi_display_chat_msg(XSTR("<Team captains have left>",649),0,0);                              
441                                 
442                                 // set ourselves to be "not quitting"
443                                 Multi_quit_game = 0;
444
445                                 // tell the users, the game has ended
446                                 send_netgame_end_error_packet(notify_code,err_code);                            
447                                 return 0;
448                         }
449
450                         send_netgame_end_error_packet(notify_code,err_code);                    
451
452                         // store the globals 
453                         Multi_endgame_notify_code = notify_code;
454                         Multi_endgame_error_code = err_code;
455                         Multi_endgame_wsa_error = wsa_error;
456
457                         // by setting this, multi_endgame_process() will know to check and see if it is ok for us to leave
458                         Multi_endgame_server_waiting = 1;                               
459                         Multi_endgame_server_wait_stamp = MULTI_ENDGAME_SERVER_WAIT;
460                 }
461                 // if i'm the client, set the error codes and leave the game now
462                 else if(!Multi_endgame_client_waiting){
463                         // if we're in the debrief state, mark down that the server has left the game
464                         if((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
465                                 multi_debrief_server_left();
466
467                                 // add a message to the chatbox
468                                 multi_display_chat_msg(XSTR("<The server has ended the game>",650),0,0);
469
470                                 // shut our reliable socket to the server down
471                                 psnet_rel_close_socket(&Net_player->reliable_socket);
472                                 Net_player->reliable_socket = INVALID_SOCKET;
473
474                                 // remove our do-notworking flag
475                                 Net_player->flags &= ~(NETINFO_FLAG_DO_NETWORKING);
476                                 
477                                 Multi_quit_game = 0;
478                                 return 0;
479                         }
480
481                         Multi_endgame_notify_code = notify_code;
482                         Multi_endgame_error_code = err_code;
483                         Multi_endgame_wsa_error = wsa_error;
484
485                         // by setting this, multi_endgame_process() will know to check and see if it is ok for us to leave
486                         Multi_endgame_client_waiting = 1;                       
487                 }
488         }               
489
490         // unset the reentrancy flag
491         Multi_quit_game = 0;
492
493         return 1;
494 }
495
496
497 // ----------------------------------------------------------------------------------------------------------
498 // MULTI ENDGAME FORWARD DEFINITIONS
499 //
500
501 // called when a given netgame is about to end completely
502 void multi_endgame_cleanup()
503 {
504         int idx;
505
506         send_leave_game_packet();                       
507
508         // flush all outgoing io, force all packets through
509         multi_io_send_buffered_packets();
510                 
511         // mark myself as disconnected
512         if(!(Game_mode & GM_STANDALONE_SERVER)){
513                 Net_player->flags &= ~(NETINFO_FLAG_CONNECTED|NETINFO_FLAG_DO_NETWORKING);
514         }
515                 
516         // this is a semi-hack so that if we're the master and we're quitting, we don't get an assert
517         if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Player_obj != NULL)){
518                 Player_obj->flags &= ~(OF_PLAYER_SHIP);
519                 obj_set_flags( Player_obj, Player_obj->flags | OF_COULD_BE_PLAYER );
520         }
521         
522         // shut my socket down (will also let the server know i've received any notifications/error from him)
523         // psnet_rel_close_socket( &(Net_player->reliable_socket) );
524
525         // 11/18/98 - DB, changed the above to kill all sockets. Its the safest thing to do
526         for(idx=0; idx<MAX_PLAYERS; idx++){
527                 psnet_rel_close_socket(&Net_players[idx].reliable_socket);
528                 Net_players[idx].reliable_socket = INVALID_SOCKET;
529         }
530
531         // set the game quitting flag in our local netgame info - this will _insure_ that even if we miss a packet or
532         // there is some sequencing error, the next time through the multi_do_frame() loop, the game will be ended
533         // Netgame.flags |= (NG_FLAG_QUITTING | NG_FLAG_ENDED);
534
535         // close all open SPX/TCP reliable sockets
536         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
537                 int idx;
538                 // do it for all players, since we're leaving anyway.
539                 for(idx=0;idx<MAX_PLAYERS;idx++){
540                         // 6/25/98 -- MWA delete all players from the game
541
542                         if ( &Net_players[idx] != Net_player ) {
543                                 delete_player( idx );
544                         }                       
545                 }
546         }       
547
548         // if we're currently in the pause state, pop back into gameplay first
549         if(gameseq_get_state() == GS_STATE_MULTI_PAUSED){
550                 gameseq_pop_state();
551         }
552
553         if(Game_mode & GM_STANDALONE_SERVER){
554                 // multi_standalone_quit_game();                
555                 multi_standalone_reset_all();
556         } else {                
557                 Player->flags |= PLAYER_FLAGS_IS_MULTI;         
558
559                 // if we're in Parallax Online mode, log back in there  
560                 gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME);           
561
562                 // if we have an error code, bring up the discon popup                                          
563                 if((Multi_endgame_notify_code != -1) || (Multi_endgame_error_code != -1) && !(Game_mode & GM_STANDALONE_SERVER)){
564                         multi_endgame_popup(Multi_endgame_notify_code,Multi_endgame_error_code,Multi_endgame_wsa_error);                        
565                 }               
566         }       
567
568         /*
569         extern CFILE *obj_stream;
570         if(obj_stream != NULL){
571                 cfclose(obj_stream);
572                 obj_stream = NULL;
573         }
574         */      
575
576         // unload the multiplayer common interface palette
577         multi_common_unload_palette();
578         
579         // reinitialize endgame stuff
580         // multi_endgame_init();
581 }
582
583 // throw up a popup with the given notification code and optional winsock code
584 void multi_endgame_popup(int notify_code,int error_code,int wsa_error)
585 {
586         char err_msg[255];
587         int flags = PF_USE_AFFIRMATIVE_ICON;    
588
589         // if there is a popup already active, just kill it
590         if(popup_active()){
591                 // if there is already a popup active, kill it
592                 popup_kill_any_active();
593
594                 Int3();
595         } else {
596                 // if there is a winsock error code, stick it on the end of the text
597                 if(wsa_error != -1){            
598                         sprintf(err_msg,NOX("WSAERROR : %d\n\n"),wsa_error);
599                         flags |= PF_TITLE_RED;
600                 } else {
601                         strcpy(err_msg,"");
602                 }
603
604                 // setup the error message string
605                 if(notify_code != MULTI_END_NOTIFY_NONE){
606                         switch(notify_code){
607                         case MULTI_END_NOTIFY_KICKED :
608                                 strcat(err_msg,XSTR("You have been kicked",651));
609                                 break;
610                         case MULTI_END_NOTIFY_SERVER_LEFT:
611                                 strcat(err_msg,XSTR("The server has left the game",652));
612                                 break;
613                         case MULTI_END_NOTIFY_FILE_REJECTED:
614                                 strcat(err_msg,XSTR("Your mission file has been rejected by the server",653));
615                                 break;
616                         case MULTI_END_NOTIFY_EARLY_END:
617                                 strcat(err_msg,XSTR("The game has ended while you were ingame joining",654));
618                                 break;
619                         case MULTI_END_NOTIFY_INGAME_TIMEOUT:
620                                 strcat(err_msg,XSTR("You have waited too long to select a ship",655));
621                                 break;
622                         case MULTI_END_NOTIFY_KICKED_BAD_XFER:
623                                 strcat(err_msg,XSTR("You were kicked because mission file xfer failed",998));
624                                 break;
625                         case MULTI_END_NOTIFY_KICKED_CANT_XFER:
626                                 strcat(err_msg,XSTR("You were kicked because you do not have the builtin mission",999));
627                                 strcat(err_msg, NOX(" "));
628                                 strcat(err_msg, Game_current_mission_filename);
629                                 break;
630                         case MULTI_END_NOTIFY_KICKED_INGAME_ENDED:
631                                 strcat(err_msg,XSTR("You were kicked because you were ingame joining a game that has ended",1000));
632                                 break;
633                         default : 
634                                 Int3();
635                         }               
636                 } else {        
637                         switch(error_code){
638                         case MULTI_END_ERROR_CONTACT_LOST :
639                                 strcat(err_msg,XSTR("Contact with server has been lost",656));
640                                 break;
641                         case MULTI_END_ERROR_CONNECT_FAIL :
642                                 strcat(err_msg,XSTR("Failed to connect to server on reliable socket",657));
643                                 break;
644                         case MULTI_END_ERROR_LOAD_FAIL :
645                                 strcat(err_msg,XSTR("Failed to load mission file properly",658));
646                                 break;                                          
647                         case MULTI_END_ERROR_INGAME_SHIP :
648                                 strcat(err_msg,XSTR("Unable to create ingame join player ship",659));
649                                 break;
650                         case MULTI_END_ERROR_INGAME_BOGUS :
651                                 strcat(err_msg,XSTR("Recevied bogus packet data while ingame joining",660));
652                                 break;
653                         case MULTI_END_ERROR_STRANS_FAIL :
654                                 strcat(err_msg,XSTR("Server transfer failed (obsolete)",661));
655                                 break;
656                         case MULTI_END_ERROR_SHIP_ASSIGN:
657                                 strcat(err_msg,XSTR("Server encountered errors trying to assign players to ships",662));
658                                 break;
659                         case MULTI_END_ERROR_HOST_LEFT:
660                                 strcat(err_msg,XSTR("Host has left the game, aborting...",663));
661                                 break;                  
662                         case MULTI_END_ERROR_XFER_FAIL:
663                                 strcat(err_msg,XSTR("There was an error receiving the mission file!",665));
664                                 break;
665                         case MULTI_END_ERROR_WAVE_COUNT:
666                                 strcat(err_msg,XSTR("The player wings Alpha, Beta, Gamma, and Zeta must have only 1 wave.  One of these wings currently has more than 1 wave.", 987));
667                                 break;
668                         case MULTI_END_ERROR_TEAM0_EMPTY:
669                                 strcat(err_msg,XSTR("All players from team 1 have left the game", 1466));
670                                 break;
671                         case MULTI_END_ERROR_TEAM1_EMPTY:
672                                 strcat(err_msg,XSTR("All players from team 2 have left the game", 1467));
673                                 break;
674                         case MULTI_END_ERROR_CAPTAIN_LEFT:
675                                 strcat(err_msg,XSTR("Team captain(s) have left the game, aborting...",664));
676                                 break;
677                         default :
678                                 Int3();
679                         }               
680                 }
681
682                 // show the popup
683                 popup(flags,1,POPUP_OK,err_msg);        
684         }
685 }
686
687 // called when server is waiting for clients to disconnect
688 int multi_endgame_server_ok_to_leave()
689 {
690         int idx,clients_gone;
691         
692         // check to see if our client disconnect timestamp has elapsed
693         if ( Multi_endgame_server_wait_stamp > 0.0f ) {
694                 Multi_endgame_server_wait_stamp -= flFrametime;
695                 if ( Multi_endgame_server_wait_stamp <= 0.0f ) {
696                         return 1;
697                 }
698         }
699                 
700         // check to see if all clients have disconnected
701         clients_gone = 1;
702         for(idx=0;idx<MAX_PLAYERS;idx++){
703                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
704                         clients_gone = 0;
705                         return 0;
706                 }
707         }
708
709         // all conditions passed
710         return 1;
711 }
712
713 // check to see if we need to be warping out (strange transition possibilities)
714 void multi_endgame_check_for_warpout()
715 {
716         int need_to_warpout = 0;
717
718         // if we're not in the process of warping out - do nothing
719         if(!(Net_player->flags & NETINFO_FLAG_WARPING_OUT)){
720                 return;
721         }
722
723         // determine if sufficient warping-out conditions exist
724         if((Game_mode & GM_IN_MISSION) &&                                                                               // if i'm still in the mission
725                 ((Netgame.game_state == NETGAME_STATE_ENDGAME)  ||                              // if the netgame ended
726                  (Netgame.game_state == NETGAME_STATE_DEBRIEF))                                 // if the netgame is now in the debriefing state
727           ) {
728                 need_to_warpout = 1;
729         }
730
731         // if we need to be warping out but are stuck in a dead popup, cancel it
732         if(need_to_warpout && (popupdead_is_active() || (Net_player->flags & NETINFO_FLAG_RESPAWNING) || (Net_player->flags & NETINFO_FLAG_OBSERVER)) ){                
733                 // flush all active pushed state
734                 multi_handle_state_special();
735
736                 // begin the warpout process            
737                 gameseq_post_event(GS_EVENT_DEBRIEF);
738
739                 // if text input mode is active, clear it
740                 multi_msg_text_flush();
741         }       
742 }