1 /*
2 
3 *************************************************************************
4 
5 ArmageTron -- Just another Tron Lightcycle Game in 3D.
6 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
7 
8 **************************************************************************
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 ***************************************************************************
25 
26 */
27 
28 
29 #include "gStuff.h"
30 #include "eSound.h"
31 #include "eGrid.h"
32 #include "eTeam.h"
33 #include "tSysTime.h"
34 #include "gGame.h"
35 #include "rTexture.h"
36 #include "gWall.h"
37 #include "rConsole.h"
38 #include "gCycle.h"
39 #include "eCoord.h"
40 #include "eTimer.h"
41 #include "gAIBase.h"
42 #include "rSysdep.h"
43 #include "rFont.h"
44 #include "uMenu.h"
45 #include "nConfig.h"
46 #include "rScreen.h"
47 #include "rViewport.h"
48 #include "rModel.h"
49 #include "uInput.h"
50 #include "ePlayer.h"
51 #include "gArena.h"
52 #include "gSpawn.h"
53 #include "uInput.h"
54 #include "uInputQueue.h"
55 #include "nNetObject.h"
56 #include "tToDo.h"
57 #include "gMenus.h"
58 #include "gCamera.h"
59 #include "gServerBrowser.h"
60 #include "gServerFavorites.h"
61 #include "gFriends.h"
62 #include "gLogo.h"
63 #include "gLanguageMenu.h"
64 #include "nServerInfo.h"
65 #include "gAICharacter.h"
66 #include "tDirectories.h"
67 #include "gTeam.h"
68 #include "gWinZone.h"
69 #include "eVoter.h"
70 #include "tRecorder.h"
71 
72 #include "gParser.h"
73 #include "tResourceManager.h"
74 #include "nAuthentication.h"
75 
76 #include <math.h>
77 #include <stdlib.h>
78 #include <string>
79 #include <fstream>
80 #include <ctype.h>
81 #include <time.h>
82 
83 #include "nSocket.h"
84 
85 #ifdef KRAWALL_SERVER
86 #include "nKrawall.h"
87 #endif
88 
89 
90 #ifndef DEDICATED
91 #include "rSDL.h"
92 #include <SDL_thread.h>
93 
94 #ifdef DEBUG
95 #ifndef WIN32
96 //#include <GL/xmesa>
97 
98 //static bool fullscreen=1;
99 #endif
100 #endif
101 #endif
102 
103 #ifdef DEBUG
104 //#define CONNECTION_STRESS
105 #endif
106 
107 tCONFIG_ENUM( gGameType );
108 tCONFIG_ENUM( gFinishType );
109 
110 // extra round pause time
111 static REAL sg_extraRoundTime = 0.0f;
112 static tSettingItem<REAL> sg_extraRoundTimeConf( "EXTRA_ROUND_TIME", sg_extraRoundTime );
113 
114 static REAL sg_lastChatBreakTime = -1.0f;
115 static tSettingItem<REAL> sg_lastChatBreakTimeConf( "LAST_CHAT_BREAK_TIME", sg_lastChatBreakTime );
116 
117 static tString mapuri("0");
118 static nSettingItem<tString> conf_mapuri("MAP_URI",mapuri);
119 
120 #define DEFAULT_MAP "Anonymous/polygon/regular/square-1.0.1.aamap.xml"
121 static tString mapfile(DEFAULT_MAP);
122 
123 static bool sg_waitForExternalScript = false;
124 static tSettingItem< bool > sg_waitForExternalScriptConf( "WAIT_FOR_EXTERNAL_SCRIPT", sg_waitForExternalScript );
125 
126 static REAL sg_waitForExternalScriptTimeout = 3;
127 static tSettingItem<REAL> sg_waitForExternalScriptTimeoutConf( "WAIT_FOR_EXTERNAL_SCRIPT_TIMEOUT", sg_waitForExternalScriptTimeout );
128 
129 static nSettingItemWatched<tString> conf_mapfile("MAP_FILE",mapfile, nConfItemVersionWatcher::Group_Breaking, 8 );
130 
131 // enable/disable sound, supporting two different pause reasons:
132 // if fromActivity is set, the pause comes from losing input focus.
133 // otherwise, it's from game state changes.
sg_SoundPause(bool pause,bool fromActivity)134 static void sg_SoundPause( bool pause, bool fromActivity )
135 {
136     static bool flags[2]={true, false};
137     flags[ fromActivity ] = pause;
138     se_SoundPause( flags[0] || flags[1] );
139 }
140 
141 // bool globalingame=false;
sg_GetCurrentTime(char const * szFormat)142 tString sg_GetCurrentTime( char const * szFormat )
143 {
144     char szTemp[128];
145     time_t     now;
146     struct tm *pTime;
147     now = time(NULL);
148     pTime = localtime(&now);
149     strftime(szTemp,sizeof(szTemp),szFormat,pTime);
150     return tString(szTemp);
151 }
152 
sg_PrintCurrentTime(char const * szFormat)153 void sg_PrintCurrentTime( char const * szFormat )
154 {
155     con << sg_GetCurrentTime(szFormat);
156 }
157 
sg_PrintCurrentDate()158 void sg_PrintCurrentDate()
159 {
160     sg_PrintCurrentTime( "%Y%m%d");
161 }
162 
sg_PrintCurrentTime()163 void sg_PrintCurrentTime()
164 {
165     sg_PrintCurrentTime( "%H%M%S" );
166 }
167 
sg_Timestamp()168 void sg_Timestamp()
169 {
170 #ifdef DEDICATED
171     sg_PrintCurrentTime( "Timestamp: %Y/%m/%d %H:%M:%S\n" );
172     if ( tRecorder::IsRunning() )
173     {
174         con << "Uptime: " << int(tSysTimeFloat()) << " seconds.\n";
175 
176 #ifdef DEBUG_X
177         // to set breakpoints at specific round starts
178         static double breakTime = 0;
179         if ( tSysTimeFloat() > breakTime )
180             st_Breakpoint();
181 #endif
182     }
183 #endif
184 }
185 
186 static REAL ded_idle=24;
187 static tSettingItem<REAL> dedicaded_idle("DEDICATED_IDLE",ded_idle);
188 
189 
190 static eWavData intro("moviesounds/intro.wav");
191 static eWavData extro("moviesounds/extro.wav");
192 
193 
194 #define MAXAI (gAICharacter::s_Characters.Len())
195 
196 #define AUTO_AI_MAXFRAC 6
197 #define AUTO_AI_WIN     3
198 #define AUTO_AI_LOSE    1
199 
gGameSettings(int a_scoreWin,int a_limitTime,int a_limitRounds,int a_limitScore,int a_numAIs,int a_minPlayers,int a_AI_IQ,bool a_autoNum,bool a_autoIQ,int a_speedFactor,int a_sizeFactor,gGameType a_gameType,gFinishType a_finishType,int a_minTeams,REAL a_winZoneMinRoundTime,REAL a_winZoneMinLastDeath)200 gGameSettings::gGameSettings(int a_scoreWin,
201                              int a_limitTime, int a_limitRounds, int a_limitScore,
202                              int a_numAIs,    int a_minPlayers,  int a_AI_IQ,
203                              bool a_autoNum, bool a_autoIQ,
204                              int a_speedFactor, int a_sizeFactor,
205                              gGameType a_gameType,  gFinishType a_finishType,
206                              int a_minTeams,
207                              REAL a_winZoneMinRoundTime, REAL a_winZoneMinLastDeath
208                             )
209         :scoreWin(a_scoreWin),
210         limitTime(a_limitTime), limitRounds(a_limitRounds), limitScore(a_limitScore),
211         numAIs(a_numAIs),       minPlayers(a_minPlayers),   AI_IQ(a_AI_IQ),
212         autoNum(a_autoNum),     autoIQ(a_autoIQ),
213         speedFactor(a_speedFactor), sizeFactor(a_sizeFactor),
214         winZoneMinRoundTime( a_winZoneMinRoundTime ),winZoneMinLastDeath( a_winZoneMinLastDeath ),
215         gameType(a_gameType),   finishType(a_finishType),
216         minTeams(a_minTeams)
217 {
218     autoAIFraction = AUTO_AI_MAXFRAC >> 1;
219 
220     maxTeams = 16;
221     minPlayersPerTeam = 1;
222     maxPlayersPerTeam = 10;
223     maxTeamImbalance = 1;
224     balanceTeamsWithAIs = true;
225     enforceTeamRulesOnQuit = false;
226 
227     wallsStayUpDelay = 2.0f;
228     wallsLength      = -1.0f;
229     explosionRadius  = 4.0f;
230 }
231 
AutoAI(bool success)232 void gGameSettings::AutoAI(bool success){
233     if (!autoNum && !autoIQ)
234         return;
235 
236     if (autoNum)
237     {
238         if (success)
239         {
240             autoAIFraction += AUTO_AI_WIN;
241             while (autoAIFraction > AUTO_AI_MAXFRAC)
242             {
243                 autoAIFraction -= AUTO_AI_MAXFRAC;
244                 if (numAIs < MAXAI) numAIs++;
245             }
246         }
247         else
248         {
249             autoAIFraction -= AUTO_AI_LOSE;
250             while (autoAIFraction < 0)
251             {
252                 autoAIFraction += AUTO_AI_MAXFRAC;
253                 if (numAIs >= 2) numAIs--;
254             }
255         }
256     }
257 
258 
259     if (autoIQ)
260     {
261         if (!autoNum)
262             AI_IQ += 4 * (success ? AUTO_AI_WIN : -AUTO_AI_LOSE);
263         else
264         {
265             int total = numAIs * numAIs * AI_IQ;
266             // try to keep total around 100: that is either 10 dumb AIs or
267             // one smart AI.
268 
269             if (success)
270                 if (total > 100)
271                     AI_IQ += AUTO_AI_WIN * 4;
272                 else
273                     AI_IQ += AUTO_AI_WIN;
274             else
275                 if (total < 100)
276                     AI_IQ -= AUTO_AI_LOSE * 4;
277                 else
278                     AI_IQ -= AUTO_AI_LOSE;
279         }
280     }
281 
282     if (AI_IQ > 100)
283         AI_IQ = 100;
284     if (AI_IQ < 0)
285         AI_IQ = 0;
286 }
287 
288 
Menu()289 void gGameSettings::Menu()
290 {
291     uMenu GameSettings("$game_settings_menu_text");
292 
293     uMenuItemReal wzmr
294     (&GameSettings,
295      "$game_menu_wz_mr_text",
296      "$game_menu_wz_mr_help",
297      winZoneMinRoundTime, (REAL) 0, (REAL) 1000, (REAL) 10 );
298 
299     uMenuItemReal wzmld
300     (&GameSettings,
301      "$game_menu_wz_ld_text",
302      "$game_menu_wz_ld_help",
303      winZoneMinLastDeath, (REAL) 0 , (REAL) 1000, (REAL) 10 );
304 
305     uMenuItemToggle team_et
306     (&GameSettings,
307      "$game_menu_balance_quit_text",
308      "$game_menu_balance_quit_help",
309      enforceTeamRulesOnQuit);
310 
311     uMenuItemToggle team_bt
312     (&GameSettings,
313      "$game_menu_balance_ais_text",
314      "$game_menu_balance_ais_help",
315      balanceTeamsWithAIs);
316 
317     uMenuItemInt team_mi
318     (&GameSettings,
319      "$game_menu_imb_text",
320      "$game_menu_imb_help",
321      maxTeamImbalance,2,10);
322 
323     uMenuItemInt team_maxp
324     (&GameSettings,
325      "$game_menu_max_players_text",
326      "$game_menu_max_players_help",
327      maxPlayersPerTeam,1,16);
328 
329     uMenuItemInt team_minp
330     (&GameSettings,
331      "$game_menu_min_players_text",
332      "$game_menu_min_players_help",
333      minPlayersPerTeam,1,16);
334 
335     uMenuItemInt team_max
336     (&GameSettings,
337      "$game_menu_max_teams_text",
338      "$game_menu_max_teams_help",
339      maxTeams,1,16);
340 
341     uMenuItemInt team_min
342     (&GameSettings,
343      "$game_menu_min_teams_text",
344      "$game_menu_min_teams_help",
345      minTeams,1,16);
346 
347     uMenuItemSelection<gFinishType> finisht
348     (&GameSettings,"$game_menu_finish_text",
349      "$game_menu_finish_help",finishType);
350     finisht.NewChoice("$game_menu_finish_expr_text",
351                       "$game_menu_finish_expr_help",
352                       gFINISH_EXPRESS);
353     finisht.NewChoice("$game_menu_finish_stop_text",
354                       "$game_menu_finish_stop_help",
355                       gFINISH_IMMEDIATELY);
356     finisht.NewChoice("$game_menu_finish_fast_text",
357                       "$game_menu_finish_fast_help",
358                       gFINISH_SPEEDUP);
359     finisht.NewChoice("$game_menu_finish_normal_text",
360                       "$game_menu_finish_normal_help",
361                       gFINISH_NORMAL);
362 
363     uMenuItemSelection<gGameType> gamet
364     (&GameSettings,"$game_menu_mode_text",
365      "$game_menu_mode_help",gameType);
366     gamet.NewChoice("$game_menu_mode_free_text",
367                     "$game_menu_mode_free_help",
368                     gFREESTYLE);
369     gamet.NewChoice("$game_menu_mode_lms_text",
370                     "$game_menu_mode_lms_help",
371                     gDUEL);
372     /*
373     	gamet.NewChoice("$game_menu_mode_team_text",
374     					"$game_menu_mode_team_help",
375     					gHUMAN_VS_AI);
376     */
377 
378     uMenuItemInt speedconf
379     (&GameSettings,
380      "$game_menu_speed_text",
381      "$game_menu_speed_help",
382      speedFactor,-10,10);
383 
384     uMenuItemInt sizeconf
385     (&GameSettings,
386      "$game_menu_size_text",
387      "$game_menu_size_help",
388      sizeFactor,-10,10);
389 
390     uMenuItemSelection<REAL> wsuconf
391     (&GameSettings,
392      "$game_menu_wallstayup_text",
393      "$game_menu_wallstayup_help",
394      wallsStayUpDelay);
395     wsuconf.NewChoice( "$game_menu_wallstayup_infinite_text",
396                        "$game_menu_wallstayup_infinite_help",
397                        -1.0f );
398     wsuconf.NewChoice( "$game_menu_wallstayup_immediate_text",
399                        "$game_menu_wallstayup_immediate_help",
400                        0.0f );
401     wsuconf.NewChoice( "$game_menu_wallstayup_halfsecond_text",
402                        "$game_menu_wallstayup_halfsecond_help",
403                        0.5f );
404     wsuconf.NewChoice( "$game_menu_wallstayup_second_text",
405                        "$game_menu_wallstayup_second_help",
406                        1.0f );
407     wsuconf.NewChoice( "$game_menu_wallstayup_2second_text",
408                        "$game_menu_wallstayup_2second_help",
409                        2.0f );
410     wsuconf.NewChoice( "$game_menu_wallstayup_4second_text",
411                        "$game_menu_wallstayup_4second_help",
412                        4.0f );
413     wsuconf.NewChoice( "$game_menu_wallstayup_8second_text",
414                        "$game_menu_wallstayup_8second_help",
415                        8.0f );
416     wsuconf.NewChoice( "$game_menu_wallstayup_16second_text",
417                        "$game_menu_wallstayup_16second_help",
418                        16.0f );
419     wsuconf.NewChoice( "$game_menu_wallstayup_32second_text",
420                        "$game_menu_wallstayup_32second_help",
421                        32.0f );
422 
423     uMenuItemSelection<REAL> wlconf
424     (&GameSettings,
425      "$game_menu_wallslength_text",
426      "$game_menu_wallslength_help",
427      wallsLength);
428     wlconf.NewChoice( "$game_menu_wallslength_infinite_text",
429                       "$game_menu_wallslength_infinite_help",
430                       -1.0f );
431     wlconf.NewChoice( "$game_menu_wallslength_25meter_text",
432                       "$game_menu_wallslength_25meter_help",
433                       25.0f );
434     wlconf.NewChoice( "$game_menu_wallslength_50meter_text",
435                       "$game_menu_wallslength_50meter_help",
436                       50.0f );
437     wlconf.NewChoice( "$game_menu_wallslength_100meter_text",
438                       "$game_menu_wallslength_100meter_help",
439                       100.0f );
440     wlconf.NewChoice( "$game_menu_wallslength_200meter_text",
441                       "$game_menu_wallslength_200meter_help",
442                       200.0f );
443     wlconf.NewChoice( "$game_menu_wallslength_300meter_text",
444                       "$game_menu_wallslength_300meter_help",
445                       300.0f );
446     wlconf.NewChoice( "$game_menu_wallslength_400meter_text",
447                       "$game_menu_wallslength_400meter_help",
448                       400.0f );
449     wlconf.NewChoice( "$game_menu_wallslength_600meter_text",
450                       "$game_menu_wallslength_600meter_help",
451                       600.0f );
452     wlconf.NewChoice( "$game_menu_wallslength_800meter_text",
453                       "$game_menu_wallslength_800meter_help",
454                       800.0f );
455     wlconf.NewChoice( "$game_menu_wallslength_1200meter_text",
456                       "$game_menu_wallslength_1200meter_help",
457                       1200.0f );
458     wlconf.NewChoice( "$game_menu_wallslength_1600meter_text",
459                       "$game_menu_wallslength_1600meter_help",
460                       1600.0f );
461 
462     uMenuItemSelection<REAL> erconf
463     (&GameSettings,
464      "$game_menu_exrad_text",
465      "$game_menu_exrad_help",
466      explosionRadius);
467     erconf.NewChoice( "$game_menu_exrad_0_text",
468                       "$game_menu_exrad_0_help",
469                       0.0f );
470     erconf.NewChoice( "$game_menu_exrad_2meters_text",
471                       "$game_menu_exrad_2meters_help",
472                       2.0f );
473     erconf.NewChoice( "$game_menu_exrad_4meters_text",
474                       "$game_menu_exrad_4meters_help",
475                       4.0f );
476     erconf.NewChoice( "$game_menu_exrad_8meters_text",
477                       "$game_menu_exrad_8meters_help",
478                       8.0f );
479     erconf.NewChoice( "$game_menu_exrad_16meters_text",
480                       "$game_menu_exrad_16meters_help",
481                       16.0f );
482     erconf.NewChoice( "$game_menu_exrad_32meters_text",
483                       "$game_menu_exrad_32meters_help",
484                       32.0f );
485     erconf.NewChoice( "$game_menu_exrad_64meters_text",
486                       "$game_menu_exrad_64meters_help",
487                       64.0f );
488     erconf.NewChoice( "$game_menu_exrad_128meters_text",
489                       "$game_menu_exrad_128meters_help",
490                       128.0f );
491 
492     uMenuItemToggle autoiqconf
493     (&GameSettings,
494      "$game_menu_autoiq_text",
495      "$game_menu_autoiq_help",
496      autoIQ);
497 
498     uMenuItemToggle autoaiconf
499     (&GameSettings,
500      "$game_menu_autoai_text",
501      "$game_menu_autoai_help",
502      autoNum);
503 
504 
505     uMenuItemInt iqconf
506     (&GameSettings,
507      "$game_menu_iq_text",
508      "$game_menu_iq_help",
509      AI_IQ, 20, 100, 10);
510 
511     uMenuItemInt mpconf
512     (&GameSettings,
513      "$game_menu_minplayers_text",
514      "$game_menu_minplayers_help",
515      minPlayers,0,MAXAI);
516 
517     uMenuItemInt aiconf
518     (&GameSettings,
519      "$game_menu_ais_text",
520      "$game_menu_ais_help",
521      numAIs,0,MAXAI);
522 
523 
524     GameSettings.Enter();
525 }
526 
527 gGameSettings singlePlayer(10,
528                            30, 10, 100000,
529                            1,   0, 30,
530                            true, true,
531                            0  ,  -3,
532                            gDUEL, gFINISH_IMMEDIATELY, 1,
533                            100000, 1000000);
534 
535 gGameSettings multiPlayer(10,
536                           30, 10, 100,
537                           0,   4, 100,
538                           false, false,
539                           0  ,  -3,
540                           gDUEL, gFINISH_IMMEDIATELY, 2,
541                           60, 30 );
542 
543 gGameSettings* sg_currentSettings = &singlePlayer;
544 
545 
546 
547 static tSettingItem<int> mp_sw("SCORE_WIN"   ,multiPlayer.scoreWin);
548 static tSettingItem<int> mp_lt("LIMIT_TIME"  ,multiPlayer.limitTime);
549 static tSettingItem<int> mp_lr("LIMIT_ROUNDS",multiPlayer.limitRounds);
550 static tSettingItem<int> mp_ls("LIMIT_SCORE" ,multiPlayer.limitScore);
551 
552 static tConfItem<int>    mp_na("NUM_AIS"     ,multiPlayer.numAIs);
553 static tConfItem<int>    mp_mp("MIN_PLAYERS" ,multiPlayer.minPlayers);
554 static tConfItem<int>    mp_iq("AI_IQ"       ,multiPlayer.AI_IQ);
555 
556 static tConfItem<bool>   mp_an("AUTO_AIS"    ,multiPlayer.autoNum);
557 static tConfItem<bool>   mp_aq("AUTO_IQ"     ,multiPlayer.autoIQ);
558 
559 static tConfItem<int>    mp_sf("SPEED_FACTOR",multiPlayer.speedFactor);
560 static tConfItem<int>    mp_zf("SIZE_FACTOR" ,multiPlayer.sizeFactor);
561 
562 static tConfItem<gGameType>    mp_gt("GAME_TYPE",multiPlayer.gameType);
563 static tConfItem<gFinishType>  mp_ft("FINISH_TYPE",multiPlayer.finishType);
564 
565 static tConfItem<REAL>   mp_wzmr("WIN_ZONE_MIN_ROUND_TIME",multiPlayer.winZoneMinRoundTime);
566 static tConfItem<REAL>   mp_wzld("WIN_ZONE_MIN_LAST_DEATH",multiPlayer.winZoneMinLastDeath);
567 
568 static tConfItem<int>    mp_tmin	("TEAMS_MIN",					multiPlayer.minTeams);
569 static tConfItem<int>    mp_tmax	("TEAMS_MAX",					multiPlayer.maxTeams);
570 static tConfItem<int>    mp_mtp		("TEAM_MIN_PLAYERS",			multiPlayer.minPlayersPerTeam);
571 static tConfItem<int>    mp_tp		("TEAM_MAX_PLAYERS",			multiPlayer.maxPlayersPerTeam);
572 static tConfItem<int>    mp_tib		("TEAM_MAX_IMBALANCE",			multiPlayer.maxTeamImbalance);
573 static tConfItem<bool>   mp_tbai	("TEAM_BALANCE_WITH_AIS",		multiPlayer.balanceTeamsWithAIs);
574 static tConfItem<bool>   mp_tboq	("TEAM_BALANCE_ON_QUIT",		multiPlayer.enforceTeamRulesOnQuit);
575 
576 static tConfItem<REAL>   mp_wsu		("WALLS_STAY_UP_DELAY"	,		multiPlayer.wallsStayUpDelay);
577 static tConfItem<REAL>   mp_wl		("WALLS_LENGTH"		    ,		multiPlayer.wallsLength     );
578 static tConfItem<REAL>   mp_er		("EXPLOSION_RADIUS"		,		multiPlayer.explosionRadius );
579 
580 static tSettingItem<int> sp_sw("SP_SCORE_WIN"   ,singlePlayer.scoreWin);
581 static tSettingItem<int> sp_lt("SP_LIMIT_TIME"  ,singlePlayer.limitTime);
582 static tSettingItem<int> sp_lr("SP_LIMIT_ROUNDS",singlePlayer.limitRounds);
583 static tSettingItem<int> sp_ls("SP_LIMIT_SCORE" ,singlePlayer.limitScore);
584 
585 static tConfItem<int>    sp_na("SP_NUM_AIS"     ,singlePlayer.numAIs);
586 static tConfItem<int>    sp_mp("SP_MIN_PLAYERS" ,singlePlayer.minPlayers);
587 static tConfItem<int>    sp_iq("SP_AI_IQ"       ,singlePlayer.AI_IQ);
588 
589 static tConfItem<bool>   sp_an("SP_AUTO_AIS"    ,singlePlayer.autoNum);
590 static tConfItem<bool>   sp_aq("SP_AUTO_IQ"     ,singlePlayer.autoIQ);
591 
592 static tConfItem<int>    sp_sf("SP_SPEED_FACTOR",singlePlayer.speedFactor);
593 static tConfItem<int>    sp_zf("SP_SIZE_FACTOR" ,singlePlayer.sizeFactor);
594 
595 static tConfItem<gGameType>    sp_gt("SP_GAME_TYPE",singlePlayer.gameType);
596 static tConfItem<gFinishType>  sp_ft("SP_FINISH_TYPE",singlePlayer.finishType);
597 
598 static tConfItem<REAL>    sp_wzmr("SP_WIN_ZONE_MIN_ROUND_TIME",singlePlayer.winZoneMinRoundTime);
599 static tConfItem<REAL>    sp_wzld("SP_WIN_ZONE_MIN_LAST_DEATH",singlePlayer.winZoneMinLastDeath);
600 
601 static tConfItem<int>    sp_tmin	("SP_TEAMS_MIN",					singlePlayer.minTeams);
602 static tConfItem<int>    sp_tmax	("SP_TEAMS_MAX",					singlePlayer.maxTeams);
603 static tConfItem<int>    sp_mtp		("SP_TEAM_MIN_PLAYERS",				singlePlayer.minPlayersPerTeam);
604 static tConfItem<int>    sp_tp		("SP_TEAM_MAX_PLAYERS",				singlePlayer.maxPlayersPerTeam);
605 static tConfItem<int>    sp_tib		("SP_TEAM_MAX_IMBALANCE",			singlePlayer.maxTeamImbalance);
606 static tConfItem<bool>   sp_tbai	("SP_TEAM_BALANCE_WITH_AIS",		singlePlayer.balanceTeamsWithAIs);
607 static tConfItem<bool>   sp_tboq	("SP_TEAM_BALANCE_ON_QUIT",		singlePlayer.enforceTeamRulesOnQuit);
608 
609 static tConfItem<REAL>   sp_wsu		("SP_WALLS_STAY_UP_DELAY"	,		singlePlayer.wallsStayUpDelay);
610 static tConfItem<REAL>   sp_wl		("SP_WALLS_LENGTH"		    ,		singlePlayer.wallsLength     );
611 static tConfItem<REAL>   sp_er		("SP_EXPLOSION_RADIUS"		,		singlePlayer.explosionRadius );
612 
GameSettingsMP()613 static void GameSettingsMP(){
614     multiPlayer.Menu();
615 }
616 
GameSettingsSP()617 static void GameSettingsSP(){
618     singlePlayer.Menu();
619 }
620 
GameSettingsCurrent()621 static void GameSettingsCurrent(){
622     sg_currentSettings->Menu();
623 }
624 
625 static REAL sg_Timeout = 5.0f;
626 static tConfItem<REAL>   sg_ctimeout("GAME_TIMEOUT"		,		sg_Timeout );
627 
628 bool sg_TalkToMaster = true;
629 static tSettingItem<bool> sg_ttm("TALK_TO_MASTER",
630                                  sg_TalkToMaster);
631 
632 class gHighscoresBase{
633     int id;
634     static tList<gHighscoresBase> highscoreList;
635 protected:
636     tArray<tString> highName;
637 
638     char const * highscore_file;
639     tOutput desc;
640     int     maxSize;
641 
642     // find the player at position i
online(int p)643     ePlayerNetID *online(int p){
644         for (int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
645             if (se_PlayerNetIDs(i)->IsHuman() && !strcmp(se_PlayerNetIDs(i)->GetUserName(),highName[p]))
646                 return se_PlayerNetIDs(i);
647 
648         return NULL;
649     }
650 
651     // i is the active player, j the passive one
swap_entries(int i,int j)652     virtual void swap_entries(int i,int j){
653         Swap(highName[i],highName[j]);
654 
655         // swap: i is now the passive player
656 
657         // send the poor guy who just dropped a message
658         ePlayerNetID *p=online(i);
659         if (p){
660             tColoredString name;
661             name << *p << tColoredString::ColorString(1,.5,.5);
662 
663             tOutput message;
664             message.SetTemplateParameter(1, static_cast<const char *>(name));
665             message.SetTemplateParameter(2, static_cast<const char *>(highName[j]));
666             message.SetTemplateParameter(3, i+1);
667             message.SetTemplateParameter(4, desc);
668 
669             if (i<j)
670                 message <<  "$league_message_rose" ;
671             else
672                 message <<  "$league_message_dropped" ;
673 
674             message << "\n";
675 
676             tString s;
677             s << message;
678             sn_ConsoleOut(s,p->Owner());
679             // con << message;
680         }
681 
682     }
683 
load_Name(std::istream & s,int i)684     void load_Name(std::istream &s,int i){
685         std::ws( s );
686 
687         // read name
688         highName[i].ReadLine( s );
689     }
690 
save_Name(std::ostream & s,int i)691     void save_Name(std::ostream &s,int i){
692         s << highName[i];
693     }
694 
695 
696 public:
697 
698     virtual void Save()=0;
699     virtual void Load()=0;
700 
701     // returns if i should stay above j
702     virtual bool inorder(int i,int j)=0;
703 
sort()704     void sort(){
705         // since single score items travel up the
706         // score ladder, this is the most efficient sort algorithm:
707 
708         for(int i=1;i<highName.Len();i++)
709             for(int j=i;j>=1 && !inorder(j-1,j);j--)
710                 swap_entries(j,j-1);
711     }
712 
713 
checkPos(int found)714     int checkPos(int found){
715         // move him up
716         int newpos=found;
717         while(newpos>0 && !inorder(newpos-1,newpos)){
718             swap_entries(newpos,newpos-1);
719             newpos--;
720         }
721 
722         // move him down
723         while(newpos<highName.Len()-1 && !inorder(newpos,newpos+1)){
724             swap_entries(newpos,newpos+1);
725             newpos++;
726         }
727 
728         //Save();
729 
730         return newpos;
731     }
732 
Find(const char * name,bool force=false)733     int Find(const char *name,bool force=false){
734         int found=highName.Len();
735         for(int i=found-1;i>=0 ;i--)
736             if(highName[i].Len()<=1 || !strcmp(highName[i],name)){
737                 found=i;
738             }
739 
740         if (force && highName[found].Len()<=1)
741             highName[found]=name;
742 
743         return found;
744     }
745 
gHighscoresBase(char const * name,char const * sd,int max=0)746     gHighscoresBase(char const * name,char const * sd,int max=0)
747             :id(-1),highscore_file(name),desc(sd),maxSize(max){
748         highscoreList.Add(this,id);
749     }
750 
~gHighscoresBase()751     virtual ~gHighscoresBase(){
752         highscoreList.Remove(this,id);
753     }
754 
greet_this(ePlayerNetID * p,tOutput & o)755     virtual void greet_this(ePlayerNetID *p,tOutput &o){
756         //    tOutput o;
757 
758         int f=Find(p->GetUserName())+1;
759         int l=highName.Len();
760 
761         o.SetTemplateParameter(1, f);
762         o.SetTemplateParameter(2, l);
763         o.SetTemplateParameter(3, desc);
764 
765         if (l>=f)
766             o << "$league_message_greet";
767         else
768             o << "$league_message_greet_new";
769 
770         //    s << o;
771     }
772 
Greet(ePlayerNetID * p,tOutput & o)773     static void Greet(ePlayerNetID *p,tOutput &o){
774         o << "$league_message_greet_intro";
775         for(int i=highscoreList.Len()-1;i>=0;i--){
776             highscoreList(i)->greet_this(p,o);
777             if (i>1)
778                 o << "$league_message_greet_sep" << " ";
779             if (i==1)
780                 o << " " << "$league_message_greet_lastsep" << " ";
781         }
782         o << ".\n";
783     }
784 
SaveAll()785     static void SaveAll(){
786         for(int i=highscoreList.Len()-1;i>=0;i--)
787             highscoreList(i)->Save();
788     }
789 
LoadAll()790     static void LoadAll(){
791         for(int i=highscoreList.Len()-1;i>=0;i--)
792             highscoreList(i)->Load();
793     }
794 };
795 
796 
GreetHighscores()797 tString GreetHighscores()
798 {
799     tOutput o;
800     gHighscoresBase::Greet(eCallbackGreeting::Greeted(),o);
801     tString s;
802     s << o;
803     return s;
804 }
805 
806 static eCallbackGreeting g(GreetHighscores);
807 
808 tList<gHighscoresBase> gHighscoresBase::highscoreList;
809 
810 template<class T>class highscores: public gHighscoresBase{
811     protected:
812     tArray<T>    high_score;
813 
swap_entries(int i,int j)814     virtual void swap_entries(int i,int j){
815         Swap(high_score[i],high_score[j]);
816         gHighscoresBase::swap_entries(i,j);
817     }
818 
819     public:
Load()820     virtual void Load(){
821         tTextFileRecorder stream( tDirectories::Var(), highscore_file );
822         int i=0;
823         while ( !stream.EndOfFile() )
824         {
825             std::stringstream s( stream.GetLine() );
826             s >> high_score[i];
827             load_Name(s,i);
828             // con << highName[i] << " " << high_score[i] << '\n';
829             i++;
830         }
831     }
832 
833     // returns if i should stay above j
inorder(int i,int j)834     virtual bool inorder(int i,int j)
835     {
836         return (highName[j].Len()<=1 || high_score[i]>=high_score[j]);
837     }
838 
839 
Save()840     virtual void Save(){
841         std::ofstream s;
842 
843         sort();
844 
845         tString filename(highscore_file);
846         if ( tRecorder::IsPlayingBack() )
847         {
848             filename += ".playback";
849         }
850 
851         if ( tDirectories::Var().Open ( s, filename ) )
852         {
853             int i=0;
854             int max=high_score.Len();
855             if (maxSize && max>maxSize)
856                 max=maxSize;
857             while (i<max && highName[i].Len()>1){
858                 tString mess;
859                 mess << high_score[i];
860                 mess.SetPos(10, false );
861                 s << mess;
862                 save_Name(s,i);
863                 s << '\n';
864                 i++;
865                 //std::cout << mess;
866                 //save_Name(std::cout,i);
867                 //std::cout << '\n';
868             }
869         }
870     }
871 
checkPos(int found,const tString & name,T score)872     void checkPos(int found,const tString &name,T score){
873         tOutput message;
874         // find the name in the list
875         bool isnew=false;
876 
877         message.SetTemplateParameter(1, name);
878         message.SetTemplateParameter(2, desc);
879         message.SetTemplateParameter(3, score);
880 
881         if (highName[found].Len()<=1){
882             highName[found]=name;
883             message << "$highscore_message_entered";
884             high_score[found]=score;
885             isnew=true;
886         }
887         else if (score>high_score[found]){
888             message << "$highscore_message_improved";
889             high_score[found]=score;
890         }
891         else
892             return;
893 
894         // move him up
895         int newpos=gHighscoresBase::checkPos(found);
896 
897         message.SetTemplateParameter(4, newpos + 1);
898 
899         if (newpos!=found || isnew)
900             if (newpos==0)
901                 message << "$highscore_message_move_top";
902             else
903                 message << "$highscore_message_move_pos";
904         else
905             if (newpos==0)
906                 message << "$highscore_message_stay_top";
907             else
908                 message << "$highscore_message_stay_pos";
909 
910         message << "\n";
911 
912         ePlayerNetID *p=online(newpos);
913         //con << message;
914         if (p)
915             sn_ConsoleOut(tString(message),p->Owner());
916         //Save();
917     }
918 
Add(ePlayerNetID * player,T AddScore)919     void Add( ePlayerNetID* player,T AddScore)
920     {
921         tASSERT( player );
922         tString const & name = player->GetUserName();
923         int f=Find(name,true);
924         checkPos(f,name,AddScore+high_score[f]);
925     }
926 
Add(eTeam * team,T AddScore)927     void Add( eTeam* team,T AddScore)
928     {
929         if ( team->NumHumanPlayers() > 0 )
930         {
931             for ( int i = team->NumPlayers() - 1 ; i>=0; --i )
932             {
933                 ePlayerNetID* player = team->Player( i );
934                 if ( player->IsHuman() )
935                 {
936                     this->Add( player, AddScore );
937                 }
938             }
939         }
940     }
941 
Check(const ePlayerNetID * player,T score)942     void Check(const ePlayerNetID* player,T score){
943         tASSERT( player );
944         tString name = player->GetUserName();
945 
946         // find the name in the list
947         int found=Find(name,true);
948         checkPos(found,name,score);
949     }
950 
highscores(char const * name,char const * sd,int max=0)951     highscores(char const * name,char const * sd,int max=0)
952             :gHighscoresBase(name,sd,max){
953         //		Load();
954     }
955 
~highscores()956     virtual ~highscores(){
957         //		Save();
958     }
959 };
960 
961 
962 
963 static REAL ladder_min_bet=1;
964 static tSettingItem<REAL> ldd_mb("LADDER_MIN_BET",
965                                  ladder_min_bet);
966 
967 static REAL ladder_perc_bet=10;
968 static tSettingItem<REAL> ldd_pb("LADDER_PERCENT_BET",
969                                  ladder_perc_bet);
970 
971 static REAL ladder_tax=1;
972 static tSettingItem<REAL> ldd_tex("LADDER_TAX",
973                                   ladder_tax);
974 
975 static REAL ladder_lose_perc_on_load=.2;
976 static tSettingItem<REAL> ldd_lpl("LADDER_LOSE_PERCENT_ON_LOAD",
977                                   ladder_lose_perc_on_load);
978 
979 static REAL ladder_lose_min_on_load=.2;
980 static tSettingItem<REAL> ldd_lml("LADDER_LOSE_MIN_ON_LOAD",
981                                   ladder_lose_min_on_load);
982 
983 static REAL ladder_gain_extra=1;
984 static tSettingItem<REAL> ldd_ga("LADDER_GAIN_EXTRA",
985                                  ladder_gain_extra);
986 
987 static float sg_gameTimeInterval=-1;
988 static tSettingItem<float> sggti("LADDERLOG_GAME_TIME_INTERVAL",
989                                  sg_gameTimeInterval);
990 
991 
992 class ladder: public highscores<REAL>{
993 public:
Load()994     virtual void Load(){
995         highscores<REAL>::Load();
996 
997         sort();
998 
999         int end=highName.Len();
1000 
1001         for(int i=highName.Len()-1;i>=0;i--){
1002 
1003             // make them lose some points
1004 
1005             REAL loss=ladder_lose_perc_on_load*high_score[i]*.01;
1006             if (loss<ladder_lose_min_on_load)
1007                 loss=ladder_lose_min_on_load;
1008             high_score[i]-=loss;
1009 
1010             if (high_score[i]<0)
1011                 end=i;
1012         }
1013 
1014         // remove the bugggers with less than 0 points
1015         highName.SetLen(end);
1016         high_score.SetLen(end);
1017     }
1018 
checkPos(int found,const tString & name,REAL score)1019     void checkPos(int found,const tString &name,REAL score){
1020         tOutput message;
1021 
1022         message.SetTemplateParameter(1, name);
1023         message.SetTemplateParameter(2, desc);
1024         message.SetTemplateParameter(3, score);
1025 
1026         // find the name in the list
1027         bool isnew=false;
1028         if (highName[found].Len()<=1){
1029             highName[found]=name;
1030             message << "$ladder_message_entered";
1031             high_score[found]=score;
1032             isnew=true;
1033         }
1034         else{
1035             REAL diff=score-high_score[found];
1036             message.SetTemplateParameter(5, static_cast<float>(fabs(diff)));
1037 
1038             if (diff>0)
1039                 message << "$ladder_message_gained";
1040             else
1041                 message << "$ladder_message_lost";
1042 
1043             high_score[found]=score;
1044         }
1045 
1046         // move him up
1047         int newpos=gHighscoresBase::checkPos(found);
1048 
1049         message.SetTemplateParameter(4, newpos + 1);
1050 
1051         if (newpos!=found || isnew)
1052             if (newpos==0)
1053                 message << "$ladder_message_move_top";
1054             else
1055                 message << "$ladder_message_move_pos";
1056         else
1057             if (newpos==0)
1058                 message << "$ladder_message_stay_top";
1059             else
1060                 message << "$ladder_message_stay_pos";
1061 
1062         message << "\n";
1063 
1064         ePlayerNetID *p=online(newpos);
1065         // con << message;
1066         if (p){
1067             sn_ConsoleOut(tString(message),p->Owner());
1068         }
1069 
1070         // Save();
1071     }
1072 
Add(const tString & name,REAL AddScore)1073     void Add(const tString &name,REAL AddScore){
1074         int found=Find(name,true);
1075         checkPos(found,name,AddScore+high_score[found]);
1076     }
1077 
1078     // ladder mechanics: what happens if someone wins?
winner(eTeam * winningTeam)1079     void winner( eTeam *winningTeam ){
1080         tASSERT( winningTeam );
1081 
1082         // AI won? bail out.
1083         if ( winningTeam->NumHumanPlayers() <= 0 )
1084         {
1085             return;
1086         }
1087 
1088         // only do something in multiplayer mode
1089         int i;
1090         int count=0;
1091 
1092         tArray<ePlayerNetID*> active;
1093 
1094         for(i=se_PlayerNetIDs.Len()-1;i>=0;i--)
1095         {
1096             ePlayerNetID* p=se_PlayerNetIDs(i);
1097 
1098             // only take enemies of the current winners (and the winners themselves) into the list
1099             if (p->IsHuman() && ( p->CurrentTeam() == winningTeam || eTeam::Enemies( winningTeam, p ) ) )
1100             {
1101                 count++;
1102                 active[active.Len()] = p;
1103             }
1104         }
1105 
1106         // only one active player? quit.
1107         if ( active.Len() <= 1 )
1108         {
1109             return;
1110         }
1111 
1112         // collect the bets
1113         tArray<int> nums;
1114         tArray<REAL> bet;
1115 
1116         REAL pot=0;
1117 
1118         for(i=active.Len()-1;i>=0;i--){
1119 
1120             nums[i]=Find(active(i)->GetUserName(),true);
1121 
1122             if (high_score[nums[i]]<0)
1123                 high_score[nums[i]]=0;
1124 
1125             bet[i]=high_score[nums[i]]*ladder_perc_bet*.01;
1126             if (bet[i]<ladder_min_bet)
1127                 bet[i]=ladder_min_bet;
1128             pot+=bet[i];
1129         }
1130 
1131         pot-=pot*ladder_tax*.01; // you have to pay to the bank :-)
1132 
1133         // now bet[i] tells us how much player nums[i] has betted
1134         // and pot is the overall bet. Add something to it, prop to
1135         // the winners ping+ping charity:
1136 
1137         // take the bet from the losers and give it to the winner
1138         for(i=active.Len()-1; i>=0; i--)
1139         {
1140             ePlayerNetID* player = active(i);
1141             if(player->CurrentTeam() == winningTeam )
1142             {
1143                 REAL pc=player->ping + player->pingCharity*.001;
1144                 if (pc<0)
1145                     pc=0;
1146                 if (pc>.3) // add sensible bounds
1147                     pc=1;
1148 
1149                 REAL potExtra = pc*ladder_gain_extra;
1150 
1151                 if ( player->Object() && player->Object()->Alive() )
1152                 {
1153                     potExtra *= 2.0f;
1154                 }
1155 
1156                 Add(player->GetUserName(), ( pot / winningTeam->NumHumanPlayers() + potExtra ) - bet[i] );
1157             }
1158             else
1159             {
1160                 Add(player->GetUserName(),-bet[i]);
1161             }
1162         }
1163     }
1164 
ladder(char const * name,char const * sd,int max=0)1165     ladder(char const * name,char const * sd,int max=0)
1166             :highscores<REAL>(name,sd,max){
1167         //		Load();
1168     }
1169 
~ladder()1170     virtual ~ladder(){
1171         //		Save();
1172     }
1173 };
1174 
1175 
1176 static highscores<int> highscore("highscores.txt","$highscore_description",100);
1177 static highscores<int> highscore_won_rounds("won_rounds.txt",
1178         "$won_rounds_description");
1179 static highscores<int> highscore_won_matches("won_matches.txt",
1180         "$won_matches_description");
1181 static ladder highscore_ladder("ladder.txt",
1182                                "$ladder_description");
1183 
1184 #define PREPARE_TIME 4
1185 
1186 static bool just_connected=true;
1187 static bool sg_singlePlayer=0;
1188 static int winner=0;
1189 static int absolute_winner=0;
1190 static REAL lastTime_gameloop=0;
1191 static REAL lastTimeTimestep=0;
1192 
1193 static int wishWinner = 0;
1194 static char const * wishWinnerMessage = "";
1195 
sg_DeclareWinner(eTeam * team,char const * message)1196 void sg_DeclareWinner( eTeam* team, char const * message )
1197 {
1198     if ( team && !winner && !wishWinner )
1199     {
1200         wishWinner = team->TeamID() + 1;
1201         wishWinnerMessage = message;
1202     }
1203 }
1204 
check_hs()1205 void check_hs(){
1206     if (sg_singlePlayer)
1207         if(se_PlayerNetIDs.Len()>0 && se_PlayerNetIDs(0)->IsHuman())
1208             highscore.Check(se_PlayerNetIDs(0),se_PlayerNetIDs(0)->Score());
1209 }
1210 
1211 
1212 static tCONTROLLED_PTR(gGame) sg_currentGame;
1213 
1214 
1215 
1216 /*
1217 static gCycle *Cycle(int id){
1218   eGameObject *object=NULL;
1219   for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
1220     if (se_PlayerNetIDs[i]->pID==id && se_PlayerNetIDs[i]->Object())
1221       object=se_PlayerNetIDs[i]->Object();
1222 
1223   return dynamic_cast<gCycle *>(object);
1224 }
1225 */
1226 
1227 #ifdef POWERPAK_DEB
1228 #include "PowerPak/poweruInput.h"
1229 #include "PowerPak/joystick.h"
1230 #endif
1231 
1232 #include "rRender.h"
1233 
1234 gArena Arena;
1235 
1236 
1237 
1238 
1239 
exit_game_grid(eGrid * grid)1240 void exit_game_grid(eGrid *grid){
1241     eSoundLocker soundLocker;
1242 
1243     grid->Clear();
1244 }
1245 
exit_game_objects(eGrid * grid)1246 void exit_game_objects(eGrid *grid){
1247     sr_con.fullscreen=true;
1248 
1249     su_prefetchInput=false;
1250 
1251     eSoundLocker soundLocker;
1252 
1253     int i;
1254     for(i=ePlayer::Num()-1;i>=0;i--){
1255         if (ePlayer::PlayerConfig(i))
1256             tDESTROY(ePlayer::PlayerConfig(i)->cam);
1257     }
1258 
1259     eGameObject::DeleteAll(grid);
1260 
1261     if (sn_GetNetState()!=nCLIENT)
1262         for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
1263             if(se_PlayerNetIDs(i))
1264                 se_PlayerNetIDs(i)->ClearObject();
1265 
1266     gNetPlayerWall::Clear();
1267 
1268     exit_game_grid(grid);
1269 }
1270 
exponent(int i)1271 REAL exponent(int i)
1272 {
1273     int abs = i;
1274     if ( abs < 0 )
1275         abs = -abs;
1276 
1277     REAL ret = 1;
1278     REAL fac = sqrtf(2);
1279 
1280     while (abs > 0)
1281     {
1282         if ( 1 == (abs & 1) )
1283             ret *= fac;
1284 
1285         fac *= fac;
1286         abs >>= 1;
1287     }
1288 
1289     if (i < 0)
1290         ret = 1/ret;
1291 
1292     return ret;
1293 }
1294 
1295 extern REAL max_player_speed;
1296 extern bool sg_axesIndicator;
1297 
init_game_grid(eGrid * grid,gParser * aParser)1298 void init_game_grid(eGrid *grid, gParser *aParser){
1299     se_ResetGameTimer();
1300     se_PauseGameTimer(true);
1301 
1302     eSoundLocker soundLocker;
1303 
1304 #ifndef DEDICATED
1305     if (sr_glOut){
1306         sr_ResetRenderState();
1307 
1308         // rSysDep::ClearGL();
1309 
1310         // glViewport (0, 0, static_cast<GLsizei>(sr_screenWidth), static_cast<GLsizei>(sr_screenHeight));
1311 
1312         // rSysDep::SwapGL();
1313     }
1314     max_player_speed = 0.0;
1315 #endif
1316 
1317     /*
1318       if(!speedup)
1319       SDL_Delay(1000);
1320     */
1321 
1322     {
1323         // let settings in the map file be executed with the rights of the person
1324         // who set the map
1325         tCurrentAccessLevel level( conf_mapfile.GetSetting().GetSetLevel(), true );
1326 
1327         Arena.PrepareGrid(grid, aParser);
1328     }
1329 
1330     absolute_winner=winner=wishWinner=0;
1331 }
1332 
sg_NumHumans()1333 int sg_NumHumans()
1334 {
1335     int humans = 0;
1336     for (int i = se_PlayerNetIDs.Len()-1; i>=0; i--)
1337     {
1338         ePlayerNetID* p = se_PlayerNetIDs(i);
1339         if (p->IsHuman() && p->IsActive() && ( bool(p->CurrentTeam()) || bool(p->NextTeam()) ) )
1340             humans++;
1341     }
1342 
1343     return humans;
1344 }
1345 
sg_NumUsers()1346 int sg_NumUsers()
1347 {
1348     // return se_PlayerNetIDs.Len() ;
1349 #ifdef DEDICATED
1350     return sn_NumUsers();
1351 #else
1352     return sn_NumUsers() + 1;
1353 #endif
1354 }
1355 
sg_copySettings()1356 static void sg_copySettings()
1357 {
1358     eTeam::minTeams					= sg_currentSettings->minTeams;
1359     eTeam::maxTeams					= sg_currentSettings->maxTeams;
1360     eTeam::maxPlayers				= (sg_currentSettings->maxPlayersPerTeam)? sg_currentSettings->maxPlayersPerTeam : 1;
1361     eTeam::minPlayers				= sg_currentSettings->minPlayersPerTeam;
1362     eTeam::maxImbalance			 	= sg_currentSettings->maxTeamImbalance;
1363     eTeam::balanceWithAIs			= sg_currentSettings->balanceTeamsWithAIs;
1364     eTeam::enforceRulesOnQuit		= sg_currentSettings->enforceTeamRulesOnQuit;
1365 
1366     gCycle::SetWallsStayUpDelay	( sg_currentSettings->wallsStayUpDelay 	);
1367     gCycle::SetWallsLength		( sg_currentSettings->wallsLength	  	);
1368     gCycle::SetExplosionRadius	( sg_currentSettings->explosionRadius 	);
1369     gCycle::SetSpeedMultiplier	( exponent( sg_currentSettings->speedFactor ) );
1370     gArena::SetSizeMultiplier 	( exponent( sg_currentSettings->sizeFactor ) );
1371 }
1372 
update_settings(bool const * goon)1373 void update_settings( bool const * goon )
1374 {
1375     if (sn_GetNetState()!=nCLIENT)
1376     {
1377 #ifdef DEDICATED
1378         // wait for players to join
1379         {
1380             bool restarted = false;
1381 
1382             REAL timeout = tSysTimeFloat() + 3.0f;
1383             while ( sg_NumHumans() <= 0 && sg_NumUsers() > 0 && ( !goon || *goon ) )
1384             {
1385                 if ( !restarted && bool(sg_currentGame) )
1386                 {
1387                     sg_currentGame->StartNewMatch();
1388                     restarted = true;
1389                 }
1390 
1391                 if ( tSysTimeFloat() > timeout )
1392                 {
1393                     tOutput o("$gamestate_wait_players");
1394                     sn_CenterMessage(o);
1395 
1396                     tOutput o2("$gamestate_wait_players_con");
1397                     sn_ConsoleOut(o2);
1398 
1399                     timeout = tSysTimeFloat() + 10.0f;
1400                 }
1401 
1402                 // do tasks
1403                 st_DoToDo();
1404                 nAuthentication::OnBreak();
1405 
1406                 // kick spectators and chatbots
1407                 nMachine::KickSpectators();
1408                 ePlayerNetID::RemoveChatbots();
1409 
1410                 // wait for network messages
1411                 sn_BasicNetworkSystem.Select( 0.1f );
1412                 gGame::NetSyncIdle();
1413 
1414                 // handle console input
1415                 sr_Read_stdin();
1416             }
1417         }
1418 
1419         if ( sg_NumUsers() <= 0 && bool( sg_currentGame ) )
1420         {
1421             sg_currentGame->NoLongerGoOn();
1422         }
1423 
1424         // count the active players
1425         int humans = sg_NumHumans();
1426 
1427         bool newsg_singlePlayer = (humans<=1);
1428 #else
1429         bool newsg_singlePlayer = (sn_GetNetState() == nSTANDALONE);
1430 #endif
1431         if (sg_singlePlayer != newsg_singlePlayer && bool( sg_currentGame ) )
1432         {
1433             sg_currentGame->StartNewMatch();
1434         }
1435         sg_singlePlayer=newsg_singlePlayer;
1436 
1437         if (sg_singlePlayer)
1438             sg_currentSettings = &singlePlayer;
1439         else
1440             sg_currentSettings = &multiPlayer;
1441 
1442         sg_copySettings();
1443     }
1444 
1445     // update team properties
1446     for (int i = eTeam::teams.Len() - 1; i>=0; --i)
1447         eTeam::teams(i)->UpdateProperties();
1448 }
1449 
1450 static int sg_spawnPointGroupSize=0;
1451 static tSettingItem< int > sg_spawnPointGroupSizeConf( "SPAWN_POINT_GROUP_SIZE", sg_spawnPointGroupSize );
1452 
init_game_objects(eGrid * grid)1453 void init_game_objects(eGrid *grid){
1454     eSoundLocker soundLocker;
1455 
1456     /*
1457       static REAL r[MAX_PLAYERS]={1,.2,.2,1};
1458       static REAL g[MAX_PLAYERS]={.2,1,.2,1};
1459       static REAL b[MAX_PLAYERS]={.2,.2,1,.2};
1460     */
1461 
1462     if (sn_GetNetState()!=nCLIENT)
1463     {
1464         // update team settings
1465         gAIPlayer::SetNumberOfAIs(sg_currentSettings->numAIs,
1466                                   sg_currentSettings->minPlayers,
1467                                   sg_currentSettings->AI_IQ);
1468 
1469         int spawnPointsUsed = 0;
1470 
1471         for(int t=eTeam::teams.Len()-1;t>=0;t--)
1472         {
1473             eTeam *team = eTeam::teams(t);
1474 
1475 
1476             // reset team color of fresh teams
1477             if ( team->RoundsPlayed() == 0 )
1478             {
1479                 team->NameTeamAfterColor( false );
1480             }
1481 
1482             // update team name
1483             team->Update();
1484 
1485             // increase round counter
1486             team->PlayRound();
1487 
1488             gSpawnPoint *spawn = Arena.LeastDangerousSpawnPoint();
1489             spawnPointsUsed++;
1490 
1491             // if the spawn points are grouped, make sure the last player is not in a goup of his
1492             // own by skipping one spawnpoint
1493             if ( ( eTeam::teams(0)->IsHuman() || eTeam::teams(0)->NumPlayers() == 1 ) && sg_spawnPointGroupSize > 2 && ( spawnPointsUsed % sg_spawnPointGroupSize == sg_spawnPointGroupSize - 1 ) )
1494             {
1495                 // the next spawn point is the last of this group. Skip it if
1496                 // either only two players are left (and the one would need to play alone)
1497                 // or we can fill the remaining groups well with one player short.
1498                 if ( t == 2 || ( ( t % ( sg_spawnPointGroupSize - 1 ) ) == 0 && t < ( sg_spawnPointGroupSize - 1 ) * ( sg_spawnPointGroupSize - 1 ) ) )
1499                 {
1500                     eCoord pos, dir;
1501                     spawn->Spawn( pos, dir );
1502                     spawn = Arena.LeastDangerousSpawnPoint();
1503                     spawnPointsUsed++;
1504                 }
1505             }
1506 
1507             int numPlayers = team->NumPlayers();
1508             for (int p = 0; p<numPlayers; ++p)
1509             {
1510                 ePlayerNetID *pni=team->Player( p );
1511 
1512                 if ( !team->IsHuman() )
1513                 {
1514                     spawn = Arena.LeastDangerousSpawnPoint();
1515                     spawnPointsUsed++;
1516                 }
1517 
1518                 //			bool isHuman = pni->IsHuman();
1519 
1520                 // don't give inactive players a cycle
1521                 if (!pni->IsActive())
1522                     continue;
1523 
1524                 eCoord pos,dir;
1525                 gCycle *cycle=NULL;
1526                 if (sn_GetNetState()!=nCLIENT){
1527 #ifdef DEBUG
1528                     //                std::cout << "spawning player " << pni->name << '\n';
1529 #endif
1530                     spawn->Spawn(pos,dir);
1531                     pni->Greet();
1532                     cycle = new gCycle(grid, pos, dir, pni);
1533                     pni->ControlObject(cycle);
1534                     nNetObject::SyncAll();
1535                 }
1536                 //    int i=se_PlayerNetIDs(p)->pID;
1537 
1538                 // se_ResetGameTimer();
1539                 // se_PauseGameTimer(true);
1540             }
1541         }
1542 
1543 
1544 #ifdef ALLOW_NO_TEAM
1545         for(int p=se_PlayerNetIDs.Len()-1;p>=0;p--){
1546             ePlayerNetID *pni=se_PlayerNetIDs(p);
1547 
1548             gSpawnPoint *spawn=Arena.LeastDangerousSpawnPoint();
1549 
1550             if ( NULL == pni->CurrentTeam() )
1551             {
1552 #ifdef KRAWALL_SERVER
1553                 // don't allow unknown players to play
1554                 if (!pni->IsAuth())
1555                     continue;
1556 #endif
1557 
1558                 // don't give inactive players a cycle
1559                 if (!pni->IsActive())
1560                     continue;
1561 
1562                 eCoord pos,dir;
1563                 gCycle *cycle=NULL;
1564                 if (sn_GetNetState()!=nCLIENT){
1565 #ifdef DEBUG
1566                     //con << "spawning player " << se_PlayerNetIDs[p]->name << '\n';
1567 #endif
1568                     st_Breakpoint();
1569 
1570                     spawn->Spawn(pos,dir);
1571                     pni->Greet();
1572                     cycle = new gCycle(grid, pos, dir, pni, 0);
1573                     pni->ControlObject(cycle);
1574                     nNetObject::SyncAll();
1575                 }
1576                 //    int i=se_PlayerNetIDs(p)->pID;
1577 
1578                 // se_ResetGameTimer();
1579                 // se_PauseGameTimer(true);
1580             }
1581         }
1582 #endif
1583     }
1584 
1585     /*
1586     for(int p=se_PlayerNetIDs.Len()-1;p>=0;p--){
1587     	ePlayerNetID *pni=se_PlayerNetIDs(p);
1588 
1589     	bool isHuman = pni->IsHuman();
1590 
1591     #ifdef KRAWALL_SERVER
1592     	// don't allow unknown players to play
1593     	if (!pni->IsAuth())
1594     		continue;
1595     #endif
1596 
1597     	// don't give inactive players a cycle
1598     	if (!pni->IsActive())
1599     		continue;
1600 
1601     	eCoord pos,dir;
1602     	gCycle *cycle=NULL;
1603     	if (sn_GetNetState()!=nCLIENT){
1604     #ifdef DEBUG
1605     		//con << "spawning player " << se_PlayerNetIDs[p]->name << '\n';
1606     #endif
1607     		spawn->Spawn(pos,dir);
1608     		pni->Greet();
1609     		cycle = new gCycle(grid, pos, dir, pni, 0);
1610     		pni->ControlObject(cycle);
1611     		nNetObject::SyncAll();
1612     	}
1613     	//    int i=se_PlayerNetIDs(p)->pID;
1614 
1615     	se_ResetGameTimer();
1616     	se_PauseGameTimer(true);
1617 
1618     	if (sg_currentSettings->gameType==gHUMAN_VS_AI){
1619     		//      if (Cycle(p))
1620     		//	Cycle(p)->team=1;
1621     		if (cycle)
1622     		{
1623     			spawn=Arena.LeastDangerousSpawnPoint();
1624     			if (isHuman)
1625     				cycle->team=2;
1626     			else
1627     			{
1628     				cycle->team=1;
1629     			}
1630     		}
1631     	}
1632     	else{
1633     #ifdef DEBUG
1634     		//con << "new eSpawnPoint.\n";
1635     #endif
1636     		if (cycle)
1637     		{
1638     			spawn=Arena.LeastDangerousSpawnPoint();
1639     			if (isHuman)
1640     				cycle->team=p + 2;
1641     			else
1642     				cycle->team=1;
1643     		}
1644     		//      if (Cycle(p))
1645     		//	Cycle(p)->team=p+1;
1646     	}
1647     }
1648     */
1649 
1650     //	spawn=Arena.LeastDangerousSpawnPoint();
1651 
1652     /*
1653     static REAL rgb[MAXAI][3]={
1654       {1,1,.2},
1655       {1,.2,1},
1656       {.2,1,1},
1657       {1,.6,.2},
1658       {.2,1,.6},
1659       {.6,.2,1},
1660       {1,.2,.6},
1661       {.6,1,.2},
1662       {1,1,1},
1663       {.2,.2,.2}
1664     };
1665     */
1666 
1667 
1668     rITexture::LoadAll();
1669     // se_ResetGameTimer();
1670     // se_PauseGameTimer(true);
1671 
1672     su_prefetchInput=true;
1673 
1674     lastTime_gameloop=lastTimeTimestep=0;
1675 
1676     // reset scores, all players that are spawned need to get ladder points
1677     ePlayerNetID::ResetScoreDifferences();
1678 }
1679 
init_game_camera(eGrid * grid)1680 void init_game_camera(eGrid *grid){
1681 #ifndef DEDICATED
1682     for(int i=ePlayer::Num()-1;i>=0;i--)
1683         if (ePlayer::PlayerIsInGame(i)){
1684             ePlayerNetID *p=ePlayer::PlayerConfig(i)->netPlayer;
1685 
1686             if ( sg_currentSettings->finishType == gFINISH_EXPRESS && ( sn_GetNetState() != nCLIENT ) )
1687                 se_ResetGameTimer( -PREPARE_TIME/5 - sg_extraRoundTime );
1688             else
1689                 se_ResetGameTimer( -PREPARE_TIME - sg_extraRoundTime );
1690 
1691             // se_PauseGameTimer(true);
1692 
1693             ePlayer::PlayerConfig(i)->cam=new gCamera(grid,
1694                                           ePlayer::PlayerViewport(i),
1695                                           p,
1696                                           ePlayer::PlayerConfig(i),
1697                                           CAMERA_SMART);
1698 
1699             lastTime_gameloop=lastTimeTimestep=0;
1700         }
1701 #else
1702     se_ResetGameTimer( -PREPARE_TIME - sg_extraRoundTime );
1703     se_PauseGameTimer(false);
1704 #endif
1705     /*
1706       for(int p=se_PlayerNetIDs.Len()-1;p>=0;p--){
1707       int i=se_PlayerNetIDs(p)->pID;
1708 
1709       se_ResetGameTimer(-PREPARE_TIME);
1710       se_PauseGameTimer(true);
1711 
1712       if (i>=0)
1713          playerConfig[i]->cam=new eCamera(PlayerViewport(i),
1714       se_PlayerNetIDs(p),CAMERA_SMART);
1715       }
1716 
1717     */
1718 }
1719 
1720 bool think=1;
1721 
1722 #ifdef DEDICATED
1723 static int sg_dedicatedFPS = 40;
1724 static tSettingItem<int> sg_dedicatedFPSConf( "DEDICATED_FPS", sg_dedicatedFPS );
1725 
1726 static REAL sg_dedicatedFPSIdleFactor = 2.0;
1727 static tSettingItem<REAL> sg_dedicatedFPSMinstepConf( "DEDICATED_FPS_IDLE_FACTOR", sg_dedicatedFPSIdleFactor );
1728 #endif
1729 
s_Timestep(eGrid * grid,REAL time,bool cam)1730 void s_Timestep(eGrid *grid, REAL time,bool cam){
1731     gNetPlayerWall::s_CopyIntoGrid();
1732     eSoundLocker locker;
1733     REAL minstep = 0;
1734 #ifdef DEDICATED
1735     minstep = 1.0/sg_dedicatedFPS;
1736 
1737     // the low possible simulation frequency, together with lazy timesteps, produces
1738     // this much possible extra time difference between gameobjects
1739     eGameObject::SetMaxLazyLag( ( 1 + sg_dedicatedFPSIdleFactor )/( sg_dedicatedFPSIdleFactor * sg_dedicatedFPS ) );
1740 #endif
1741     eGameObject::s_Timestep(grid, time, minstep );
1742 
1743     if (cam)
1744         eCamera::s_Timestep(grid, time);
1745 
1746     lastTimeTimestep=time;
1747 }
1748 
1749 #ifndef DEDICATED
RenderAllViewports(eGrid * grid)1750 void RenderAllViewports(eGrid *grid){
1751     rViewportConfiguration *conf=rViewportConfiguration::CurrentViewportConfiguration();
1752 
1753     if(sr_glOut){
1754         sr_ResetRenderState();
1755 
1756         // enable distance based fog
1757         /*
1758         glFogi( GL_FOG_MODE, GL_EXP );
1759         glFogf( GL_FOG_DENSITY, .01/gArena::SizeMultiplier() );
1760         GLfloat black[3]={0,0,0};
1761         glFogfv( GL_FOG_COLOR, black );
1762         glEnable( GL_FOG );
1763         */
1764 
1765         const tList<eCamera>& cameras = grid->Cameras();
1766 
1767         for(int i=cameras.Len()-1;i>=0;i--){
1768             int p=sr_viewportBelongsToPlayer[i];
1769             conf->Select(i);
1770             rViewport *act=conf->Port(i);
1771             if (act && ePlayer::PlayerConfig(p))
1772                 ePlayer::PlayerConfig(p)->Render();
1773             else con << "hey! viewport " << i << " does not exist!\n";
1774         }
1775 
1776         // glDisable( GL_FOG );
1777     }
1778 
1779 #ifdef POWERPAK_DEB
1780     if (pp_out){
1781         eGameObject::PPDisplayAll();
1782         PD_ShowDoubleBuffer();
1783     }
1784 #endif
1785 }
1786 #endif
1787 
1788 
Render(eGrid * grid,REAL time,bool swap=true)1789 void Render(eGrid *grid, REAL time, bool swap=true){
1790 #ifdef DEBUG
1791     //  eFace::UpdateVisAll(10);
1792 #endif
1793     //  se_debugExt=0;
1794 
1795 #ifndef DEDICATED
1796     if (sr_glOut){
1797         static bool lastMoviePack=sg_MoviePack();
1798         if(lastMoviePack!=sg_MoviePack())
1799         {
1800             lastMoviePack=sg_MoviePack();
1801             rDisplayList::ClearAll();
1802         }
1803 
1804         RenderAllViewports(grid);
1805 
1806         sr_ResetRenderState(true);
1807         gLogo::Display();
1808 
1809         if (swap){
1810             rSysDep::SwapGL();
1811             rSysDep::ClearGL();
1812         }
1813     }
1814     else
1815     {
1816         if ( swap )
1817             rSysDep::SwapGL();
1818 
1819         tDelay( sn_defaultDelay );
1820     }
1821 #endif
1822 }
1823 
1824 #ifdef DEDICATED
cp()1825 static void cp(){
1826     std::ofstream s;
1827 
1828     if ( tDirectories::Var().Open(s, "players.txt") ){
1829         if (se_PlayerNetIDs.Len()>0)
1830             s << tColoredString::RemoveColors(ePlayerNetID::Ranking( -1, false ));
1831         else{
1832             tOutput o;
1833 
1834             int count=0;
1835             for(int i=MAXCLIENTS;i>0;i--)
1836                 if (sn_Connections[i].socket)
1837                     count++;
1838             if (count==0)
1839                 o << "$online_activity_nobody";
1840             else if (count==1)
1841                 o << "$online_activity_onespec";
1842             else
1843             {
1844                 o.SetTemplateParameter(1, count);
1845                 o << "$online_activity_manyspec";
1846             }
1847             o << "\n";
1848             s << o;
1849         }
1850     }
1851 }
1852 #endif
1853 
1854 static eLadderLogWriter sg_gameEndWriter("GAME_END", true);
1855 
own_game(nNetState enter_state)1856 static void own_game( nNetState enter_state ){
1857     tNEW(gGame);
1858     se_MakeGameTimer();
1859     max_player_speed = 0.0;
1860     sg_EnterGame( enter_state );
1861 
1862     // write scores one last time
1863     ePlayerNetID::LogScoreDifferences();
1864     ePlayerNetID::UpdateSuspensions();
1865     ePlayerNetID::UpdateShuffleSpamTesters();
1866     sg_gameEndWriter.write();
1867 
1868     sg_currentGame=NULL;
1869     se_KillGameTimer();
1870 }
1871 
singlePlayer_game()1872 static void singlePlayer_game(){
1873     sn_SetNetState(nSTANDALONE);
1874 
1875     update_settings();
1876     ePlayerNetID::CompleteRebuild();
1877 
1878     own_game( nSTANDALONE );
1879 }
1880 
sg_HostGame()1881 void sg_HostGame(){
1882     {
1883         // create a game to check map once
1884         tJUST_CONTROLLED_PTR< gGame > game = tNEW(gGame);
1885         game->Verify();
1886     }
1887 
1888     if (sg_TalkToMaster)
1889     {
1890         nServerInfo::TellMasterAboutMe( gServerBrowser::CurrentMaster() );
1891     }
1892 
1893     sn_SetNetState(nSERVER);
1894 
1895     update_settings();
1896     ePlayerNetID::CompleteRebuild();
1897 
1898     tAdvanceFrame();
1899 
1900     //#ifndef DEBUG
1901 #ifdef DEDICATED
1902     static double startTime=tSysTimeFloat();
1903 
1904     if ( sg_NumUsers() == 0)
1905     {
1906         cp();
1907         con << tOutput("$online_activity_napping") << "\n";
1908         sg_Timestamp();
1909 
1910         int counter = -1;
1911 
1912         int numPlayers = 0;
1913 
1914         while(numPlayers == 0 &&
1915                 (ded_idle <= 0.0f || tSysTimeFloat()<startTime + ded_idle * 3600 ) && !uMenu::quickexit ){
1916             sr_Read_stdin();
1917             st_DoToDo();
1918             gGame::NetSyncIdle();
1919 
1920             sn_BasicNetworkSystem.Select( 1.0f );
1921 
1922             // std::cout the players who are logged in:
1923             numPlayers = sg_NumUsers();
1924             /*
1925             			for (int i = se_PlayerNetIDs.Len()-1; i>=0; i--)
1926             				if (se_PlayerNetIDs(i)->IsAuth())
1927             					numPlayers++;
1928             */
1929 
1930             if (counter <= 0)
1931             {
1932                 // restart network, just in case we lost the input socket
1933                 sn_BasicNetworkSystem.AccessListener().Listen(false);
1934                 sn_BasicNetworkSystem.AccessListener().Listen(true);
1935                 counter = 50;
1936             }
1937         }
1938 
1939         if (sg_NumUsers() <= 0 && ded_idle > 0.0f &&
1940                 tSysTimeFloat()>= startTime + ded_idle * 3600 )
1941         {
1942             sg_Timestamp();
1943             con << "Server exiting due to DEDICATED_IDLE after " << (tSysTimeFloat() - startTime)/3600 << " hours.\n";
1944             uMenu::quickexit = uMenu::QuickExit_Total;
1945         }
1946     }
1947     cp();
1948 
1949     if (!uMenu::quickexit)
1950 #endif
1951         //#endif
1952         own_game( nSERVER );
1953 
1954     sn_SetNetState(nSTANDALONE);
1955 
1956     // close listening sockets
1957     sn_BasicNetworkSystem.AccessListener().Listen(false);
1958 }
1959 
1960 static tString sg_roundCenterMessage("");
1961 static tConfItemLine sn_roundCM_ci("ROUND_CENTER_MESSAGE",sg_roundCenterMessage);
1962 
1963 static tString sg_roundConsoleMessage("");
1964 static tConfItemLine sn_roundCcM1_ci("ROUND_CONSOLE_MESSAGE",sg_roundConsoleMessage);
1965 
1966 static bool sg_RequestedDisconnection = false;
1967 
sg_NetworkError(const tOutput & title,const tOutput & message,REAL timeout)1968 static bool sg_NetworkError( const tOutput& title, const tOutput& message, REAL timeout )
1969 {
1970     tOutput message2 ( message );
1971 
1972     if ( sn_DenyReason.Len() > 2 )
1973     {
1974         message2.AddLiteral("\n\n");
1975         message2.AddLocale("network_kill_preface");
1976         message2.AddLiteral("\n");
1977         message2.AddLiteral(sn_DenyReason);
1978     }
1979 
1980     nServerInfoBase * redirect = sn_PeekRedirectTo();
1981     if ( redirect )
1982     {
1983         message2.Append( tOutput( "$network_redirect", redirect->GetConnectionName(), (int)redirect->GetPort() ) );
1984     }
1985 
1986     return tConsole::Message( title, message2, timeout );
1987 }
1988 
1989 // revert settings to defaults in the current scope
1990 class gSettingsReverter
1991 {
1992 public:
gSettingsReverter()1993     gSettingsReverter()
1994     {
1995         // restore default configuration of network settings, saving your settings
1996         nConfItemBase::s_SaveValues();
1997         nConfItemBase::s_RevertToDefaults();
1998     }
1999 
~gSettingsReverter()2000     ~gSettingsReverter()
2001     {
2002         // restore saved values
2003         nConfItemBase::s_RevertToSavedValues();
2004     }
2005 };
2006 
2007 // receive network data
sg_Receive()2008 void sg_Receive()
2009 {
2010     sn_Receive();
2011     if ( sn_GetNetState() == nSERVER )
2012     {
2013         // servers should ignore data sent to the control socket. It probably would be better
2014         // to close the socket altogether, but that opens another can of worms.
2015         sn_DiscardFromControlSocket();
2016     }
2017 }
2018 
sg_StopQuickExit()2019 static void sg_StopQuickExit()
2020 {
2021     st_DoToDo();
2022 
2023     // stop quick exit to game level
2024     if ( uMenu::quickexit == uMenu::QuickExit_Game )
2025     {
2026         uMenu::quickexit = uMenu::QuickExit_Off;
2027     }
2028 }
2029 
2030 // return code: false if there was an error or abort
ConnectToServerCore(nServerInfoBase * server)2031 bool ConnectToServerCore(nServerInfoBase *server)
2032 {
2033     tASSERT( server );
2034 
2035     ePlayerNetID::ClearAll();
2036 
2037     // revert to default settings, restore current vlaues on exit
2038     gSettingsReverter reverter;
2039 
2040     sn_bigBrotherString = renderer_identification;
2041 
2042     nNetObject::ClearAll();
2043 
2044     just_connected=true;
2045 
2046     rViewport::Update(MAX_PLAYERS);
2047     // ePlayerNetID::Update();
2048 
2049     bool to=sr_textOut;
2050 
2051     {
2052 #ifndef DEDICATED
2053     rSysDep::SwapGL();
2054     rSysDep::ClearGL();
2055     rSysDep::SwapGL();
2056     rSysDep::ClearGL();
2057     eSoundLocker locker;
2058 #endif
2059 
2060     sr_con.autoDisplayAtNewline=true;
2061     sr_con.fullscreen=true;
2062 
2063     sr_textOut=true;
2064 
2065     tOutput o;
2066 
2067     nConnectError error = nOK;
2068 
2069     nNetObject::ClearAll();
2070 
2071     o.SetTemplateParameter(1, server->GetName());
2072     o << "$network_connecting_to_server";
2073     con << o;
2074     error = server->Connect();
2075 
2076     switch (error)
2077     {
2078     case nABORT:
2079         return false;
2080         break;
2081     case nOK:
2082         break;
2083     case nTIMEOUT:
2084         sg_NetworkError("$network_message_timeout_title", "$network_message_timeout_inter", 20);
2085         return false;
2086         break;
2087 
2088     case nDENIED:
2089         sg_NetworkError("$network_message_denied_title", sn_DenyReason.Len() > 2 ? "$network_message_denied_inter2" : "$network_message_denied_inter", 20);
2090         return false;
2091         break;
2092     }
2093     }
2094 
2095     // ePlayerNetID::CompleteRebuild();
2096 
2097     if (sn_GetNetState()==nCLIENT){
2098         REAL endTime=tSysTimeFloat()+30;
2099         con << tOutput("$network_connecting_gamestate");
2100         while (!sg_currentGame && tSysTimeFloat()<endTime && (sn_GetNetState() != nSTANDALONE)){
2101             tAdvanceFrame();
2102             sg_Receive();
2103             nNetObject::SyncAll();
2104             tAdvanceFrame();
2105             sn_SendPlanned();
2106             st_DoToDo();
2107 
2108 #ifndef DEDICATED
2109             rSysDep::SwapGL();
2110             rSysDep::ClearGL();
2111 #endif
2112 
2113             sn_Delay();
2114         }
2115         if (sg_currentGame){
2116             sr_con.autoDisplayAtNewline=false;
2117             sr_con.fullscreen=false;
2118 
2119             con << tOutput("$network_syncing_gamestate");
2120             sg_EnterGame( nCLIENT );
2121         }
2122         else{
2123             //con << "Timeout. Try again!\n";
2124             sg_NetworkError("$network_message_lateto_title", "$network_message_lateto_inter", 20);
2125         }
2126     }
2127 
2128     bool ret = true;
2129 
2130     sn_SetNetState(nSTANDALONE);
2131 
2132     sg_currentGame = NULL;
2133     nNetObject::ClearAll();
2134     ePlayerNetID::ClearAll();
2135 
2136     if (!sg_RequestedDisconnection && uMenu::quickexit != uMenu::QuickExit_Total )
2137     {
2138         sg_StopQuickExit();
2139 
2140         switch (sn_GetLastError())
2141         {
2142         case nOK:
2143             ret = sg_NetworkError("$network_message_abortconn_title",
2144                                   "$network_message_abortconn_inter", 20);
2145             break;
2146         default:
2147             ret = sg_NetworkError("$network_message_lostconn_title",
2148                                   "$network_message_lostconn_inter", 20);
2149             break;
2150         }
2151     }
2152 
2153     sr_con.autoDisplayAtNewline=false;
2154     sr_con.fullscreen=false;
2155 
2156     sr_textOut=to;
2157 
2158     return ret;
2159 }
2160 
ConnectToServer(nServerInfoBase * server)2161 void ConnectToServer(nServerInfoBase *server)
2162 {
2163     bool to = sr_textOut;
2164 
2165     bool okToRedirect = ConnectToServerCore( server );
2166 
2167     // REAL redirections = 0;
2168     // double lastTime = tSysTimeFloat();
2169 
2170     // check for redirection
2171     while( okToRedirect )
2172     {
2173         std::auto_ptr< nServerInfoBase > redirectTo( sn_GetRedirectTo() );
2174 
2175         // abort loop
2176         if ( !(&(*redirectTo)) )
2177         {
2178             break;
2179         }
2180 
2181         okToRedirect = ConnectToServerCore( redirectTo.get() );
2182 
2183         /*
2184         // redirection spam chain protection, allow one redirection every 30 seconds. Should
2185         // be short enough to allow hacky applications (server-to-server teleport), but
2186         // long enough to allow players to escape via the menu or at least shift-esc.
2187         static const REAL timeout = 30;
2188         static const REAL maxRedirections = 5;
2189 
2190         redirections = redirections + 1;
2191         if ( redirections > maxRedirections )
2192         {
2193             break;
2194         }
2195 
2196         // decay spam protection
2197         double newTime = tSysTimeFloat();
2198         REAL dt = newTime - lastTime;
2199         lastTime = newTime;
2200         redirections *= 1/(1 + dt * maxRedirections * timeout );
2201         */
2202     }
2203 
2204     sr_con.autoDisplayAtNewline=false;
2205     sr_con.fullscreen=false;
2206 
2207     sn_SetNetState(nSTANDALONE);
2208 
2209     sg_currentGame = NULL;
2210     nNetObject::ClearAll();
2211     ePlayerNetID::ClearAll();
2212 
2213     sr_textOut=to;
2214 
2215     sg_StopQuickExit();
2216 }
2217 
2218 static tConfItem<int> mor("MAX_OUT_RATE",sn_maxRateOut);
2219 static tConfItem<int> mir("MAX_IN_RATE",sn_maxRateIn);
2220 
2221 
2222 static tConfItem<int> pc("PING_CHARITY",pingCharity);
2223 
2224 
2225 uMenu *sg_IngameMenu=NULL;
2226 uMenu *sg_HostMenu  =NULL;
2227 
ret_to_MainMenu()2228 void ret_to_MainMenu(){
2229     sg_RequestedDisconnection = true;
2230 
2231     if (sg_HostMenu)
2232         sg_HostMenu->Exit();
2233 
2234     if (sg_IngameMenu)
2235         sg_IngameMenu->Exit();
2236 
2237     sr_con.fullscreen=true;
2238     sr_con.autoDisplayAtNewline=true;
2239 
2240     if(sg_currentGame)
2241         sg_currentGame->NoLongerGoOn();
2242 
2243     sn_SetNetState(nSTANDALONE);
2244     max_player_speed = 0.0;
2245 
2246     uMenu::SetIdle(NULL);
2247 }
2248 
2249 
2250 
net_options()2251 void net_options(){
2252     uMenu net_menu("$network_opts_text");
2253 
2254     uMenuItemInt high_port
2255     (&net_menu,"$network_opts_maxport_text",
2256      "$network_opts_maxport_help",
2257      gServerBrowser::highPort,1024,65536);
2258 
2259     uMenuItemInt low_port
2260     (&net_menu,"$network_opts_minport_text",
2261      "$network_opts_minport_help",
2262      gServerBrowser::lowPort,1024,65536);
2263 
2264     /*
2265     	uMenuItemFunction delpw(&net_menu,
2266     							"$network_opts_deletepw_text",
2267     							"$network_opts_deletepw_help",
2268     							&se_DeletePasswords);
2269 
2270     	uMenuItemSelection<int> storepw(&net_menu,
2271     									"$login_storepw_text",
2272     									"$login_storepw_help",
2273     									se_PasswordStorageMode);
2274     	storepw.NewChoice("$login_storepw_dont_text",
2275     					  "$login_storepw_dont_help",
2276     					  -1);
2277     	storepw.NewChoice("$login_storepw_mem_text",
2278     					  "$login_storepw_mem_help",
2279     					  0);
2280     	storepw.NewChoice("$login_storepw_disk_text",
2281     					  "$login_storepw_disk_help",
2282     					  1);
2283     */
2284 
2285     /*
2286       uMenuItemToggle master
2287       (&net_menu,"$network_opts_master_text",
2288       "$network_opts_master_help",
2289       sg_TalkToMaster);
2290     */
2291 
2292 
2293     uMenuItemInt out_rate
2294     (&net_menu,"$network_opts_outrate_text",
2295      "$network_opts_outrate_help",
2296      sn_maxRateOut,1,20);
2297 
2298 
2299     uMenuItemInt in_rate
2300     (&net_menu,"$network_opts_inrate_text",
2301      "$network_opts_inrate_help",
2302      sn_maxRateIn,1,20);
2303 
2304 
2305     uMenuItemToggle po2
2306     (&net_menu,"$network_opts_predict_text",
2307      "$network_opts_predict_help",
2308      sr_predictObjects);
2309 
2310     uMenuItemToggle lm2
2311     (&net_menu,"$network_opts_lagometer_text",
2312      "$network_opts_lagometer_help",
2313      sr_laggometer);
2314 
2315     uMenuItemToggle ai
2316     (&net_menu,"$network_opts_axesindicator_text",
2317      "$network_opts_axesindicator_help",
2318      sg_axesIndicator);
2319 
2320 
2321     uMenuItemInt p_s
2322     (&net_menu,"$network_opts_pingchar_text",
2323      "$network_opts_pingchar_help",
2324      pingCharity,0,300,20);
2325 
2326     net_menu.Enter();
2327 
2328     if (gServerBrowser::lowPort > gServerBrowser::highPort)
2329         gServerBrowser::highPort = gServerBrowser::lowPort;
2330 }
2331 
sg_HostGameMenu()2332 void sg_HostGameMenu(){
2333     uMenu net_menu("$network_host_text");
2334 
2335     sg_HostMenu = &net_menu;
2336 
2337     uMenuItemInt port(&net_menu, "$network_host_port_text",
2338                       "$network_host_port_help"
2339                       ,reinterpret_cast<int &>(sn_serverPort), gServerBrowser::lowPort, gServerBrowser::highPort);
2340 
2341     uMenuItemString serverName
2342     (&net_menu,"$network_host_name_text",
2343      "$network_host_name_help",
2344      sn_serverName);
2345 
2346     uMenuItemFunction settings1
2347     (&net_menu,
2348      "$game_settings_menu_text",
2349      "$game_settings_menu_help",
2350      &GameSettingsMP);
2351 
2352     uMenuItemFunction serv
2353     (&net_menu,"$network_host_host_text",
2354      "$network_host_host_help",&sg_HostGame);
2355 
2356     net_menu.ReverseItems();
2357     net_menu.SetSelected(0);
2358     net_menu.Enter();
2359 
2360     sg_HostMenu = NULL;
2361 }
2362 
2363 #ifndef DEDICATED
2364 class gNetIdler: public rSysDep::rNetIdler
2365 {
2366 public:
Wait()2367     virtual bool Wait() //!< wait for something to do, return true if there is work
2368     {
2369         return sn_BasicNetworkSystem.Select( 0.1 );
2370     }
Do()2371     virtual void Do()  //!< do the work.
2372     {
2373         tAdvanceFrame();
2374         sg_Receive();
2375         sn_SendPlanned();
2376     }
2377 };
2378 #endif
2379 
net_game()2380 void net_game(){
2381 #ifndef DEDICATED
2382     uMenu net_menu("$network_menu_text");
2383 
2384     uMenuItemFunction cust
2385     (&net_menu,"$network_custjoin_text",
2386      "$network_custjoin_help",&gServerFavorites::CustomConnectMenu);
2387 
2388     uMenuItemFunction mas
2389     (&net_menu,"$masters_menu",
2390      "$masters_menu_help",&gServerFavorites::AlternativesMenu);
2391 
2392     uMenuItemFunction fav
2393     (&net_menu,"$bookmarks_menu",
2394      "$bookmarks_menu_help",&gServerFavorites::FavoritesMenu);
2395 
2396     uMenuItemFunction bud
2397     (&net_menu,"$friends_menu",
2398      "$friends_menu_help",&gFriends::FriendsMenu);
2399 
2400     uMenuItemFunction opt
2401     (&net_menu,"$network_opts_text",
2402      "$network_opts_help",&net_options);
2403 
2404     /*
2405       uMenuItemFunction serv
2406       (&net_menu,"$network_host_text",
2407       "$network_host_help",&sg_HostGameMenu);
2408     */
2409 
2410     uMenuItemFunction lan
2411     (&net_menu,"$network_menu_lan_text",
2412      "$network_menu_lan_help",&gServerBrowser::BrowseLAN);
2413 
2414     uMenuItemFunction inter
2415     (&net_menu,"$network_menu_internet_text",
2416      "$network_menu_internet_help",&gServerBrowser::BrowseMaster);
2417 
2418     gNetIdler idler;
2419     // rSysDep::StartNetSyncThread( &idler );
2420     net_menu.Enter();
2421     rSysDep::StopNetSyncThread();
2422 #endif
2423 }
2424 
2425 
2426 
StartNewMatch()2427 static void StartNewMatch(){
2428     if (sg_currentGame)
2429         sg_currentGame->StartNewMatch();
2430     if (sn_GetNetState()!=nCLIENT){
2431         sn_CenterMessage("$gamestate_reset_center");
2432         sn_ConsoleOut("$gamestate_reset_console");
2433     }
2434 }
2435 
StartNewMatch_conf(std::istream &)2436 static void StartNewMatch_conf(std::istream &){
2437     StartNewMatch();
2438 }
2439 
2440 static tConfItemFunc snm("START_NEW_MATCH",&StartNewMatch_conf);
2441 
2442 #ifdef DEDICATED
Quit_conf(std::istream &)2443 static void Quit_conf(std::istream &){
2444 
2445     if ( sg_currentGame )
2446     {
2447         sg_currentGame->NoLongerGoOn();
2448     }
2449 
2450     // mark end of recording
2451     tRecorder::Playback("END");
2452     tRecorder::Record("END");
2453     uMenu::quickexit = uMenu::QuickExit_Total;
2454 }
2455 
2456 static tConfItemFunc quit_conf("QUIT",&Quit_conf);
2457 static tConfItemFunc exit_conf("EXIT",&Quit_conf);
2458 #endif
2459 
2460 void st_PrintPathInfo(tOutput &buf);
2461 
PlayerLogIn()2462 static void PlayerLogIn()
2463 {
2464     ePlayer::LogIn();
2465 
2466     if ( sg_IngameMenu )
2467     {
2468         sg_IngameMenu->Exit();
2469     }
2470 
2471     con << tOutput( "$player_authenticate_action" );
2472 }
2473 
sg_DisplayVersionInfo()2474 void sg_DisplayVersionInfo() {
2475     tOutput versionInfo;
2476     versionInfo << "$version_info_version" << "\n";
2477     st_PrintPathInfo(versionInfo);
2478     versionInfo << "$version_info_misc_stuff";
2479     sg_FullscreenMessage("$version_info_title", versionInfo, 1000);
2480 }
2481 
MainMenu(bool ingame)2482 void MainMenu(bool ingame){
2483     //	update_settings();
2484 
2485     if (ingame)
2486         sr_con.SetHeight(2);
2487 
2488     gLogo::SetDisplayed(true);
2489 
2490     tOutput gametitle;
2491     if (!ingame)
2492         gametitle << "$game_menu_text";
2493     else
2494         gametitle << "$game_menu_ingame_text";
2495 
2496     uMenu game_menu(gametitle);
2497 
2498     uMenuItemFunction *reset=NULL;
2499 
2500     if(ingame && sn_GetNetState()!=nCLIENT){
2501         reset=new uMenuItemFunction
2502               (&game_menu,"$game_menu_reset_text",
2503                "$game_menu_reset_help",
2504                &StartNewMatch);
2505     }
2506 
2507     uMenuItemFunction *settings1=NULL;
2508 
2509     if (!ingame)
2510         settings1=tNEW(uMenuItemFunction)
2511                   (&game_menu,
2512                    "$game_settings_menu_text",
2513                    "$game_settings_menu_help",
2514                    &GameSettingsSP);
2515     else
2516         settings1=tNEW(uMenuItemFunction)
2517                   (&game_menu,
2518                    "$game_settings_menu_text",
2519                    "$game_settings_menu_help",
2520                    &GameSettingsCurrent);
2521 
2522     uMenuItemFunction *connect=NULL,*start=NULL,*sound=NULL;
2523 
2524     if (!ingame){
2525         start= new uMenuItemFunction(&game_menu,"$game_menu_start_text",
2526                                      "$game_menu_start_help",&singlePlayer_game);
2527         connect=new uMenuItemFunction
2528                 (&game_menu,
2529                  "$network_menu_text",
2530                  "$network_menu_help",
2531                  &net_game);
2532 
2533     }
2534 
2535     tOutput title;
2536     title.SetTemplateParameter( 1, sn_programVersion );
2537     if (!ingame)
2538         title << "$main_menu_text";
2539     else
2540         title << "$ingame_menu_text";
2541 
2542     uMenu MainMenu(title,false);
2543 
2544     if (ingame)
2545         sg_IngameMenu = &MainMenu;
2546 
2547     char const * extitle,* exhelp;
2548     if (!ingame){
2549         extitle="$main_menu_exit_text";
2550         exhelp="$main_menu_exit_help";
2551     }
2552     else{
2553         extitle="$ingame_menu_exit_text";
2554         exhelp="$ingame_menu_exit_help";
2555     }
2556 
2557     uMenuItemExit exx(&MainMenu,extitle,
2558                       exhelp);
2559 
2560     uMenuItemFunction *return_to_main=NULL;
2561     if (ingame){
2562         if (sn_GetNetState()==nSTANDALONE)
2563             return_to_main=new uMenuItemFunction
2564                            (&MainMenu,"$game_menu_exit_text",
2565                             "$game_menu_exit_help",
2566                             &ret_to_MainMenu);
2567         else if (sn_GetNetState()==nCLIENT)
2568             return_to_main=new uMenuItemFunction
2569                            (&MainMenu,"$game_menu_disconnect_text",
2570                             "$game_menu_disconnect_help",
2571                             &ret_to_MainMenu);
2572         else
2573             return_to_main=new uMenuItemFunction
2574                            (&MainMenu,
2575                             "$game_menu_shutdown_text",
2576                             "game_menu_shutdown_help",
2577                             &ret_to_MainMenu);
2578     }
2579 
2580     uMenuItemFunction * auth = 0;
2581     static nVersionFeature authentication( 15 );
2582     if ( sn_GetNetState() == nCLIENT && ingame && authentication.Supported(0) )
2583     {
2584         auth =tNEW(uMenuItemFunction)(&MainMenu,
2585                                       "$player_authenticate_text",
2586                                       "$player_authenticate_help",
2587                                       &PlayerLogIn );
2588     }
2589 
2590     uMenuItemFunction abb(&MainMenu,
2591                           "$main_menu_about_text",
2592                           "$main_menu_about_help",
2593                           &sg_DisplayVersionInfo);
2594 
2595 
2596     uMenu Settings("$system_settings_menu_text");
2597 
2598     uMenuItemSubmenu subm_settings
2599     (&MainMenu,&Settings,
2600      "$system_settings_menu_help");
2601 
2602 
2603     uMenuItem* team = NULL;
2604     if ( ingame )
2605     {
2606         team = tNEW( uMenuItemFunction) ( &MainMenu,
2607                                           "$team_menu_title",
2608                                           "$team_menu_help",
2609                                           &gTeam::TeamMenu );
2610     }
2611 
2612     uMenuItemFunction *se_PlayerMenu=NULL;
2613 
2614     //   if (!ingame)
2615     se_PlayerMenu= new uMenuItemFunction
2616                    (&MainMenu,"$player_mainmenu_text",
2617                     "$player_mainmenu_help",
2618                     &sg_PlayerMenu);
2619 
2620     uMenuItemFunction *player_police=NULL;
2621     uMenuItemFunction *voting=NULL;
2622     if ( ingame && sn_GetNetState() != nSTANDALONE )
2623     {
2624         player_police = tNEW( uMenuItemFunction )( &MainMenu, "$player_police_text", "$player_police_help", ePlayerNetID::PoliceMenu );
2625 
2626         if ( eVoter::VotingPossible() )
2627             voting = tNEW( uMenuItemFunction )( &MainMenu, "$voting_menu_text", "$voting_menu_help", eVoter::VotingMenu );
2628     }
2629 
2630     uMenu misc("$misc_menu_text");
2631 
2632     //  misc.SetCenter(.25);
2633 
2634     uMenuItemFunction language
2635     (&misc,"$language_menu_title",
2636      "$language_menu_help",
2637      &sg_LanguageMenu);
2638 
2639     uMenuItemFunction global_key
2640     (&misc,"$misc_global_key_text",
2641      "$misc_global_key_help",
2642      &su_InputConfigGlobal);
2643 
2644     uMenuItemToggle wrap
2645     (&misc,"$misc_menuwrap_text",
2646      "$misc_menuwrap_help",
2647      uMenu::wrap);
2648 
2649     uMenuItemToggle to2
2650     (&misc,"$misc_textout_text",
2651      "$misc_textout_help",sr_textOut);
2652 
2653 
2654 
2655     uMenuItemToggle mp
2656     (&misc,"$misc_moviepack_text",
2657      "$misc_moviepack_help",sg_moviepackUse);
2658 
2659 
2660     uMenuItemSubmenu misc_sm
2661     (&Settings,&misc,
2662      "$misc_menu_help");
2663 
2664     sound = new uMenuItemFunction
2665             (&Settings,"$sound_menu_text",
2666              "$sound_menu_help",&se_SoundMenu);
2667 
2668     uMenuItemSubmenu subm
2669     (&Settings,&sg_screenMenu,
2670      "$display_settings_menu_help");
2671 
2672     uMenuItemSubmenu *gamemenuitem = NULL;
2673     if (sn_GetNetState() != nCLIENT)
2674     {
2675         char const * gamehelp;
2676         if (!ingame)
2677             gamehelp="$game_menu_main_help";
2678         else
2679             gamehelp="$game_menu_ingame_help";
2680 
2681         gamemenuitem = tNEW(uMenuItemSubmenu) (&MainMenu,
2682                                                &game_menu,
2683                                                gamehelp
2684                                               );
2685     }
2686 
2687     if (!ingame)
2688     {
2689         rViewport::Update(MAX_PLAYERS);
2690         // ePlayerNetID::Update();
2691     }
2692 
2693     MainMenu.Enter();
2694 
2695     if (settings1)
2696         delete settings1;
2697     if (team)
2698         delete team;
2699     if (gamemenuitem)
2700         delete gamemenuitem;
2701     if (sound)
2702         delete sound;
2703     if (connect)
2704         delete connect;
2705     if (start)
2706         delete start;
2707     if (return_to_main)
2708         delete return_to_main;
2709     if (se_PlayerMenu)
2710         delete se_PlayerMenu;
2711     if (reset)
2712         delete reset;
2713     if ( player_police )
2714         delete player_police;
2715     if ( voting )
2716         delete voting;
2717 
2718     if (ingame)
2719         sg_IngameMenu=NULL;
2720 
2721     if (ingame)
2722     {
2723         gLogo::SetDisplayed(false);
2724         sr_con.SetHeight(7);
2725     }
2726 
2727     if ( auth )
2728     {
2729         delete auth;
2730     }
2731 }
2732 
2733 
2734 
2735 // Game states:
2736 
2737 #define GS_CREATED 0        // newborn baby
2738 #define GS_TRANSFER_SETTINGS	7
2739 #define GS_CREATE_GRID			10
2740 #define GS_CREATE_OBJECTS		20
2741 #define GS_TRANSFER_OBJECTS		30
2742 #define GS_CAMERA				35
2743 #define GS_SYNCING				40
2744 #define GS_SYNCING2				41
2745 #define GS_SYNCING3				42
2746 #define GS_PLAY					50
2747 #define GS_DELETE_OBJECTS		60
2748 #define GS_DELETE_GRID			70
2749 #define GS_STATE_MAX			80
2750 
sg_AbeforeB(int stateA,int stateB)2751 static bool sg_AbeforeB( int stateA, int stateB )
2752 {
2753     return ( ( GS_STATE_MAX + stateA - stateB ) % GS_STATE_MAX > GS_STATE_MAX >> 2 );
2754 }
2755 
2756 #ifndef DEDICATED
2757 static void ingame_menu_cleanup();
ingame_menu()2758 static void ingame_menu()
2759 {
2760     //    globalingame=true;
2761     try
2762     {
2763         se_ChatState( ePlayerNetID::ChatFlags_Menu, true );
2764         if (sn_GetNetState()==nSTANDALONE)
2765             se_PauseGameTimer(true);
2766         MainMenu(true);
2767     }
2768     catch( ... )
2769     {
2770         ingame_menu_cleanup();
2771         ret_to_MainMenu();
2772         sg_IngameMenu=NULL;
2773         sg_HostMenu=NULL;
2774         throw;
2775     }
2776     ingame_menu_cleanup();
2777 }
2778 
ingame_menu_cleanup()2779 static void ingame_menu_cleanup()
2780 {
2781     if (sn_GetNetState()==nSTANDALONE)
2782         se_PauseGameTimer(false);
2783     se_ChatState(ePlayerNetID::ChatFlags_Menu, false);
2784     if ((bool(sg_currentGame) && sg_currentGame->GetState()!=GS_PLAY))
2785         //      || se_PlayerNetIDs.Len()==0)
2786     {
2787         rViewport::Update(MAX_PLAYERS);
2788         ePlayerNetID::Update();
2789     }
2790     //    globalingame=false;
2791 }
2792 #endif
2793 
2794 
2795 static nNOInitialisator<gGame> game_init(310,"game");
2796 
CreatorDescriptor() const2797 nDescriptor &gGame::CreatorDescriptor() const{
2798     return game_init;
2799 }
2800 
2801 
Init()2802 void gGame::Init(){
2803     grid = tNEW(eGrid());
2804     state=GS_CREATED;
2805     stateNext=GS_TRANSFER_SETTINGS;
2806     goon=true;
2807 
2808     sg_currentGame=this;
2809 #ifdef DEBUG
2810     // con << "Game created.\n";
2811 #endif
2812     aParser = tNEW(gParser)(&Arena, grid);
2813 
2814     rounds = 0;
2815     StartNewMatchNow();
2816 }
2817 
2818 
NoLongerGoOn()2819 void gGame::NoLongerGoOn(){
2820     goon=false;
2821     //	stateNext=GS_DELETE_OBJECTS;
2822     if (sn_GetNetState()==nSERVER)
2823         RequestSync();
2824 }
2825 
2826 // console with filter for remote admin output redirection
2827 class gMapLoadConsoleFilter:public tConsoleFilter{
2828 public:
gMapLoadConsoleFilter()2829     gMapLoadConsoleFilter()
2830     {
2831     }
2832 
~gMapLoadConsoleFilter()2833     ~gMapLoadConsoleFilter()
2834     {
2835     }
2836 
2837     tColoredString message_; // the console log for the error message
2838 private:
2839     // we want to come first
DoGetPriority() const2840     virtual int DoGetPriority() const{ return -100; }
2841 
2842     // don't actually filter; take line and add it to the potential error message
DoFilterLine(tString & line)2843     virtual void DoFilterLine( tString &line )
2844     {
2845         message_ << line << "\n";
2846     }
2847 };
2848 
sg_ParseMap(gParser * aParser,tString mapfile)2849 static void sg_ParseMap ( gParser * aParser, tString mapfile )
2850 {
2851     FILE* mapFD = NULL;
2852 
2853     gMapLoadConsoleFilter consoleLog;
2854 #ifdef DEBUG
2855     /*
2856       con << "Message stats:\n";
2857       for(int i=0;i<40;i++){
2858       con << i << ":" << sent_per_messid[i] << '\n';
2859       sent_per_messid[i]=0;
2860       }
2861     */
2862 
2863     con << tOutput( "$map_file_loading", mapfile );
2864 #endif
2865     mapFD = tResourceManager::openResource(mapuri, mapfile);
2866 
2867     if (!mapFD || !aParser->LoadAndValidateMapXML("", mapFD, mapfile))
2868     {
2869         if (mapFD)
2870             fclose(mapFD);
2871 
2872         tOutput errorMessage( sn_GetNetState() == nCLIENT ? "$map_file_load_failure_server" : "$map_file_load_failure_self", mapfile );
2873 
2874 #ifndef DEDICATED
2875         errorMessage << "\nLog:\n" << consoleLog.message_;
2876 #endif
2877 
2878         tOutput errorTitle("$map_file_load_failure_title");
2879 
2880         if ( sn_GetNetState() != nSTANDALONE )
2881         {
2882             throw tGenericException( errorMessage, errorTitle );
2883         }
2884 
2885         mapFD = tResourceManager::openResource("", DEFAULT_MAP);
2886         if (!mapFD || !aParser->LoadAndValidateMapXML("", mapFD, DEFAULT_MAP)) {
2887             if (mapFD)
2888                 fclose(mapFD);
2889             errorMessage << "$map_file_load_failure_default";
2890             throw tGenericException( errorMessage, errorTitle );
2891         }
2892     }
2893 
2894     if (mapFD)
2895         fclose(mapFD);
2896 }
2897 
sg_ParseMap(gParser * aParser)2898 static void sg_ParseMap ( gParser * aParser )
2899 {
2900     sg_ParseMap(aParser, mapfile);
2901 }
2902 
Verify()2903 void gGame::Verify()
2904 {
2905     // test map and load map settings
2906     sg_ParseMap( aParser );
2907     init_game_grid(grid, aParser);
2908     Arena.LeastDangerousSpawnPoint();
2909     exit_game_grid(grid);
2910 }
2911 
gGame()2912 gGame::gGame(){
2913     synced_ = true;
2914     gLogo::SetDisplayed(false);
2915     if (sn_GetNetState()!=nCLIENT)
2916         RequestSync();
2917     Init();
2918 }
2919 
gGame(nMessage & m)2920 gGame::gGame(nMessage &m):nNetObject(m){
2921     synced_ = false;
2922     Init();
2923 }
2924 
2925 
~gGame()2926 gGame::~gGame(){
2927 #ifdef DEBUG
2928     //con << "deleting game...\n";
2929 #endif
2930     //stateNext=GS_CREATE_GRID;
2931     //StateUpdate();
2932 
2933     tASSERT( sg_currentGame != this );
2934 
2935     exit_game_objects(grid);
2936     grid->Clear();
2937     ePlayerNetID::ThrowOutDisconnected();
2938 
2939     delete aParser;
2940 }
2941 
2942 
WriteSync(nMessage & m)2943 void gGame::WriteSync(nMessage &m){
2944     nNetObject::WriteSync(m);
2945     m.Write(stateNext);
2946 #ifdef DEBUG
2947     //con << "Wrote game state " << stateNext << ".\n";
2948 #endif
2949 }
2950 
ReadSync(nMessage & m)2951 void gGame::ReadSync(nMessage &m){
2952     nNetObject::ReadSync(m);
2953 
2954     unsigned short newState;
2955     m.Read(newState);
2956 
2957 #ifdef DEBUG
2958     con << "Read gamestate " << newState << ".\n";
2959 #endif
2960 
2961     // only update the state if it does not go back a few steps
2962     if ( sg_AbeforeB( stateNext, newState ) )
2963         stateNext = newState;
2964 
2965 #ifdef DEBUG
2966     //con << "Read game state " << stateNext << ".\n";
2967 #endif
2968 }
2969 
2970 
NetSync()2971 void gGame::NetSync(){
2972     // find end of recording in playback
2973     if ( tRecorder::Playback("END") )
2974     {
2975         tRecorder::Record("END");
2976         uMenu::quickexit=uMenu::QuickExit_Total;
2977     }
2978 
2979 #ifdef DEDICATED
2980     if (!sr_glOut && ePlayer::PlayerConfig(0)->cam)
2981         tERR_ERROR_INT("Someone messed with the camera!");
2982 #endif
2983     sg_Receive();
2984     nNetObject::SyncAll();
2985     tAdvanceFrame();
2986     sn_SendPlanned();
2987 }
2988 
NetSyncIdle()2989 void gGame::NetSyncIdle(){
2990     NetSync();
2991     sn_Delay();
2992 }
2993 
2994 unsigned short client_gamestate[MAXCLIENTS+2];
2995 
client_gamestate_handler(nMessage & m)2996 static void client_gamestate_handler(nMessage &m){
2997     unsigned short state;
2998     m.Read( state );
2999     client_gamestate[m.SenderID()] = state;
3000 	#ifdef DEBUG
3001     //	con << "User " << m.SenderID() << " is in Mode " << state << '\n';
3002 	#endif
3003 }
3004 
3005 static nDescriptor client_gs(311,client_gamestate_handler,"client_gamestate");
3006 
3007 
3008 
SyncState(unsigned short state)3009 void gGame::SyncState(unsigned short state){
3010     if (sn_GetNetState()==nSERVER){
3011 
3012         bool firsttime=true;
3013         bool goon=true;
3014         REAL timeout=tSysTimeFloat()+sg_Timeout*.1;
3015 
3016         sn_Sync(sg_Timeout*.1,true);
3017         NetSyncIdle();
3018 
3019         while(goon && tSysTimeFloat()<timeout){
3020             /*
3021               if (sg_currentGame)
3022               sg_currentGame->RequestSync();
3023             */
3024 
3025             sn_Sync(sg_Timeout*.1,true);
3026             NetSyncIdle();
3027 
3028             goon=false;
3029             for(int i=MAXCLIENTS;i>0;i--)
3030                 if ( sn_Connections[i].socket )
3031                 {
3032                     int clientState = client_gamestate[i];
3033                     if ( sg_AbeforeB( clientState, state ) )
3034                     {
3035                         goon=true;
3036                     }
3037                 }
3038             if (goon && firsttime){
3039                 firsttime=false;
3040 #ifdef DEBUG
3041                 con << "Waiting for users to enter gamestate " << state << '\n';
3042                 if (sn_Connections[1].ackPending>2)
3043                     con << "Ack_pending=" << sn_Connections[1].ackPending << ".\n";
3044 #endif
3045             }
3046         }
3047     }
3048 }
3049 
3050 
SetState(unsigned short act,unsigned short next)3051 void gGame::SetState(unsigned short act,unsigned short next){
3052     state=act;
3053 #ifdef DEBUG
3054     //con << "Entering gamestate " << state
3055     //<< ", next state: " << next << '\n';
3056 #endif
3057 
3058     if (sn_GetNetState()==nSERVER){
3059         RequestSync();
3060         NetSyncIdle();
3061         SyncState(state);
3062     }
3063 
3064     if (stateNext!=next){
3065         if (sn_GetNetState()!=nCLIENT || goon==false){
3066 
3067             stateNext=next;
3068 
3069             if (sn_GetNetState()==nSERVER){
3070                 RequestSync();
3071                 NetSyncIdle();
3072             }
3073         }
3074     }
3075 }
3076 
3077 // from nNetwork.C
3078 extern REAL planned_rate_control[MAXCLIENTS+2];
3079 extern REAL sent_per_messid[100];
3080 
3081 static REAL lastdeath=0;
3082 static bool roundOver=false;   // flag set when the round winner is declared
3083 
sg_VoteMenuIdle()3084 static void sg_VoteMenuIdle()
3085 {
3086     if ( !uMenu::MenuActive() )
3087     {
3088         se_ChatState( ePlayerNetID::ChatFlags_Menu, true );
3089         eVoter::VotingMenu();
3090         se_ChatState( ePlayerNetID::ChatFlags_Menu, false );
3091     }
3092 }
3093 
3094 static eLadderLogWriter sg_newRoundWriter("NEW_ROUND", true);
3095 static eLadderLogWriter sg_newMatchWriter("NEW_MATCH", true);
3096 static eLadderLogWriter sg_waitForExternalScriptWriter("WAIT_FOR_EXTERNAL_SCRIPT", true);
3097 
StateUpdate()3098 void gGame::StateUpdate(){
3099 
3100     //	if (state==GS_CREATED)
3101     //		stateNext=GS_CREATE_GRID;
3102 
3103     while ( sg_AbeforeB( state, stateNext ) && goon )
3104     {
3105 	#ifdef CONNECTION_STRESS
3106         if ( sg_ConnectionStress )
3107         {
3108             tRandomizer & randomizer = tRandomizer::GetInstance();
3109             int random = randomizer.Get( 16 );
3110             if ( random == 0 )
3111             {
3112                 sg_RequestedDisconnection = true;
3113                 //						sn_SetNetState( nSTANDALONE );
3114                 goon = false;
3115             }
3116 
3117             se_ChatState( ePlayerNetID::ChatFlags_Away, 1 );
3118         }
3119 	#endif
3120 
3121         // unsigned short int mes1 = 1, mes2 = 1, mes3 = 1, mes4 = 1, mes5 = 1;
3122 
3123         switch(state){
3124         case GS_DELETE_GRID:
3125             // sr_con.autoDisplayAtNewline=true;
3126 
3127             con << tOutput("$gamestate_deleting_grid");
3128             //				sn_ConsoleOut(sg_roundCenterMessage + "\n");
3129             sn_CenterMessage(sg_roundCenterMessage);
3130 
3131             //				for (unsigned short int mycy = 0; mycy > sg_roundConsoleMessage5.Len(); c++)
3132 
3133             exit_game_objects(grid);
3134             nNetObject::ClearAllDeleted();
3135 
3136             if (goon)
3137                 SetState(GS_TRANSFER_SETTINGS,GS_CREATE_GRID);
3138             else
3139                 state=GS_CREATE_GRID;
3140             break;
3141         case GS_CREATED:
3142         case GS_TRANSFER_SETTINGS:
3143             // sr_con.autoDisplayAtNewline=true;
3144 
3145             // transfer game settings
3146             if ( nCLIENT != sn_GetNetState() )
3147             {
3148                 update_settings( &goon );
3149                 ePlayerNetID::RemoveChatbots();
3150             }
3151 
3152             rViewport::Update(MAX_PLAYERS);
3153 
3154             // log scores before players get renamed
3155             ePlayerNetID::LogScoreDifferences();
3156             ePlayerNetID::UpdateSuspensions();
3157             ePlayerNetID::UpdateShuffleSpamTesters();
3158             sg_newRoundWriter.write();
3159 
3160             // kick spectators
3161             nMachine::KickSpectators();
3162 
3163             // rename players as per request
3164             if ( synced_ && sn_GetNetState() != nSERVER )
3165                 ePlayerNetID::Update();
3166 
3167             // delete game objects again (in case new ones were spawned)
3168             exit_game_objects(grid);
3169 
3170             // if the map has changed on the server side, verify it
3171             static tString lastMapfile( DEFAULT_MAP );
3172             if ( nCLIENT != sn_GetNetState() && mapfile != lastMapfile )
3173             {
3174                 try
3175                 {
3176                     Verify();
3177                 }
3178                 catch (tException const & e)
3179                 {
3180                     // clean up
3181                     exit_game_grid( grid );
3182 
3183                     // inform user of generic errors
3184                     tConsole::Message( e.GetName(), e.GetDescription(), 120000 );
3185 
3186                     // revert map
3187                     con << tOutput( "$map_file_reverting", lastMapfile );
3188                     conf_mapfile.Set( lastMapfile );
3189                 }
3190             }
3191 
3192             nConfItemBase::s_SendConfig(false);
3193 
3194             // wait extra long for the clients to delete the grid; they really need to be
3195             // synced this time
3196             sn_Sync( sg_Timeout*.4, true );
3197 
3198             SetState(GS_CREATE_GRID,GS_CAMERA);
3199             break;
3200 
3201         case GS_CREATE_GRID:
3202             // sr_con.autoDisplayAtNewline=true;
3203 
3204             sg_ParseMap( aParser );
3205 
3206             sn_Statistics();
3207             sg_Timestamp();
3208 
3209             con << tOutput("$gamestate_creating_grid");
3210 
3211             tAdvanceFrame();
3212 
3213             exit_game_grid(grid);
3214             init_game_grid(grid, aParser);
3215 
3216             nNetObject::ClearAllDeleted();
3217 
3218             SetState(GS_CREATE_OBJECTS,GS_CAMERA);
3219             break;
3220         case GS_CREATE_OBJECTS:
3221             // con << "Creating objects...\n";
3222 
3223             lastdeath = -100;
3224             roundOver = false;
3225 
3226             // rename players as per request
3227             if ( synced_ )
3228                 ePlayerNetID::Update();
3229 
3230             init_game_objects(grid);
3231 
3232             ePlayerNetID::RankingLadderLog();
3233 
3234             // do round begin stuff
3235             {
3236                 const tList<eGameObject>& gameObjects = Grid()->GameObjects();
3237                 for (int i=gameObjects.Len()-1;i>=0;i--)
3238                 {
3239                     eGameObject * e = gameObjects(i);
3240                     if ( e )
3241                     {
3242                         e->OnRoundBegin();
3243                     }
3244                 }
3245             }
3246 
3247             // do the first analysis of the round, now is the time to get it used to the number of teams
3248             Analysis( -1000 );
3249 
3250             s_Timestep(grid, se_GameTime(), false);
3251             SetState(GS_TRANSFER_OBJECTS,GS_CAMERA);
3252 
3253             if (sn_GetNetState() == nCLIENT && just_connected){
3254                 sn_Sync(sg_Timeout*.1,true);
3255                 sn_Sync(sg_Timeout*.1,true);
3256             }
3257             just_connected=false;
3258 
3259             break;
3260         case GS_TRANSFER_OBJECTS:
3261             // con << "Transferring objects...\n";
3262             rITexture::LoadAll();
3263             // se_ResetGameTimer();
3264             // se_PauseGameTimer(true);
3265 
3266 
3267             // push all data
3268             if(sn_GetNetState()==nSERVER){
3269                 bool goon=true;
3270                 double timeout=tSysTimeFloat()+sg_Timeout*.4;
3271                 while(goon && tSysTimeFloat()<timeout){
3272                     NetSyncIdle();
3273                     goon=false;
3274                     for(int i=MAXCLIENTS;i>0;i--)
3275                         if (sn_Connections[i].socket)
3276                             for(int j=sn_netObjects.Len()-1;j>=0;j--)
3277                                 if (sn_netObjects(j) &&
3278                                         !sn_netObjects(j)->HasBeenTransmitted(i) &&
3279                                         sn_netObjects(j)->syncRequested(i))
3280                                     goon=true;
3281                 }
3282                 if (tSysTimeFloat()<timeout)
3283                     con << tOutput("$gamestate_done");
3284                 else{
3285                     con << tOutput("$gamestate_timeout_intro");
3286                     for(int i=MAXCLIENTS;i>0;i--)
3287                         if (sn_Connections[i].socket)
3288                             for(int j=sn_netObjects.Len()-1;j>=0;j--)
3289                                 if (sn_netObjects(j) && !sn_netObjects(j)->HasBeenTransmitted(i))
3290                                 {
3291                                     tOutput o;
3292                                     tString name;
3293                                     sn_netObjects(j)->PrintName( name );
3294                                     o.SetTemplateParameter(1, i);
3295                                     o.SetTemplateParameter(2, j);
3296                                     o.SetTemplateParameter(3, name);
3297                                     o << "$gamestate_timeout_message";
3298                                     con << o;
3299                                 }
3300                     con << "\n\n\n";
3301                 }
3302             }
3303 
3304             // wait for players to ready
3305             if ( sn_GetNetState() == nSERVER )
3306                 while ( ePlayerNetID::WaitToLeaveChat() )
3307                 {
3308                     NetSyncIdle();
3309                     se_SyncGameTimer();
3310                 }
3311 
3312             SetState(GS_CAMERA,GS_SYNCING);
3313             break;
3314         case GS_CAMERA:
3315             // con << "Setting camera position...\n";
3316             rITexture::LoadAll();
3317             // se_ResetGameTimer(-PREPARE_TIME);
3318             // se_PauseGameTimer(true);
3319             init_game_camera(grid);
3320             SetState(GS_SYNCING,GS_PLAY);
3321             break;
3322         case GS_SYNCING:
3323             // con << "Syncing timer...\n";
3324             rITexture::LoadAll();
3325             SetState(GS_PLAY,GS_PLAY);
3326             if (sn_GetNetState()!=nCLIENT){
3327                 if (rounds<=0){
3328                     sn_ConsoleOut("$gamestate_resetnow_console");
3329                     StartNewMatchNow();
3330                     sn_CenterMessage("$gamestate_resetnow_center");
3331                     se_SaveToScoreFile("$gamestate_resetnow_log");
3332                 }
3333 
3334                 tOutput mess;
3335                 if (rounds < sg_currentSettings->limitRounds)
3336                 {
3337                     mess.SetTemplateParameter(1, rounds+1);
3338                     mess.SetTemplateParameter(2, sg_currentSettings->limitRounds);
3339                     mess << "$gamestate_newround_console";
3340 
3341                     if (strlen(sg_roundConsoleMessage) > 2)
3342                         sn_ConsoleOut(sg_roundConsoleMessage + "\n");
3343                 }
3344                 else
3345                     mess << "$gamestate_newround_goldengoal";
3346                 sn_ConsoleOut(mess);
3347 
3348                 se_SaveToScoreFile("$gamestate_newround_log");
3349             }
3350             //con << ePlayerNetID::Ranking();
3351 
3352             se_PauseGameTimer(false);
3353             sg_SoundPause( false, false );
3354             se_SyncGameTimer();
3355             sr_con.fullscreen=false;
3356             sr_con.autoDisplayAtNewline=false;
3357 
3358             break;
3359         case GS_PLAY:
3360             sg_SoundPause( true, false );
3361             sr_con.autoDisplayAtNewline=false;
3362 #ifdef DEDICATED
3363             {
3364                 // save current players into a file
3365                 cp();
3366 
3367                 if ( sg_NumUsers() <= 0 )
3368                     goon = 0;
3369 
3370                 Analysis(0);
3371 
3372                 // wait for external script to end its work if needed
3373                 REAL timeout = tSysTimeFloat() + sg_waitForExternalScriptTimeout;
3374                 if ( sg_waitForExternalScript )
3375                 {
3376                     sg_waitForExternalScriptWriter.write();
3377                     // REAL waitingSince = tSysTimeFloat();
3378                 }
3379                 while ( sg_waitForExternalScript && timeout > tSysTimeFloat())
3380                 {
3381                     sr_Read_stdin();
3382 
3383                     // wait for network messages
3384                     sn_BasicNetworkSystem.Select( 0.1f );
3385                     gGame::NetSyncIdle();
3386                 }
3387 
3388                 {
3389                     // default include files are executed at owner level
3390                     tCurrentAccessLevel level( tAccessLevel_Owner, true );
3391 
3392                     std::ifstream s;
3393 
3394                     // load contents of everytime.cfg for real
3395                     static const tString everytime("everytime.cfg");
3396                     if ( tConfItemBase::OpenFile(s, everytime, tConfItemBase::Config ) )
3397                         tConfItemBase::ReadFile(s);
3398 
3399                     s.close();
3400 
3401                     if ( tConfItemBase::OpenFile(s, everytime, tConfItemBase::Var ) )
3402                         tConfItemBase::ReadFile(s);
3403                 }
3404             }
3405 #endif
3406             // pings should not count as much in the between-round phase
3407             nPingAverager::SetWeight(1E-20);
3408 
3409             se_UserShowScores(false);
3410 
3411             //con.autoDisplayAtNewline=true;
3412             sr_con.fullscreen=true;
3413             SetState(GS_DELETE_OBJECTS,GS_DELETE_GRID);
3414             break;
3415         case GS_DELETE_OBJECTS:
3416 
3417             winDeathZone_ = NULL;
3418 
3419             rViewport::Update(MAX_PLAYERS);
3420             if ( synced_ && sn_GetNetState() != nSERVER )
3421                 ePlayerNetID::Update();
3422 
3423             // sr_con.autoDisplayAtNewline=true;
3424 
3425             gHighscoresBase::SaveAll();
3426             con << tOutput("$gamestate_deleting_objects");
3427             exit_game_objects(grid);
3428             st_ToDo( sg_VoteMenuIdle );
3429             nNetObject::ClearAllDeleted();
3430             SetState(GS_DELETE_GRID,GS_TRANSFER_SETTINGS);
3431             break;
3432         default:
3433             break;
3434         }
3435 
3436         // now would be a good time to tend for pending tasks
3437         nAuthentication::OnBreak();
3438 
3439         if (sn_GetNetState()==nSERVER){
3440             NetSyncIdle();
3441             RequestSync();
3442             NetSyncIdle();
3443         }
3444         else if (sn_GetNetState()==nCLIENT){ // inform the server of our progress
3445             NetSyncIdle();
3446             nMessage *m=new nMessage(client_gs);
3447             m->Write(state);
3448             m->Send(0);
3449 #ifdef DEBUG
3450             //			con << "sending gamestate " << state << '\n';
3451 #endif
3452             NetSyncIdle();
3453         }
3454     }
3455 }
3456 
3457 // uncomment to activate respawning
3458 // #define RESPAWN_HACK
3459 
3460 #ifdef RESPAWN_HACK
3461 // Respawns cycles (crude test)
sg_Respawn(REAL time,eGrid * grid,gArena & arena)3462 static void sg_Respawn( REAL time, eGrid *grid, gArena & arena )
3463 {
3464     for ( int i = se_PlayerNetIDs.Len()-1; i >= 0; --i )
3465     {
3466         ePlayerNetID *p = se_PlayerNetIDs(i);
3467 
3468         if ( !p->CurrentTeam() )
3469             continue;
3470 
3471         eGameObject *e=p->Object();
3472 
3473         if ( ( !e || ( !e->Alive() && e->DeathTime() < time - .5 ) ) && sn_GetNetState() != nCLIENT )
3474         {
3475             eCoord pos,dir;
3476             if ( e )
3477             {
3478                 dir = e->Direction();
3479                 pos = e->Position();
3480                 eWallRim::Bound( pos, 1 );
3481                 eCoord displacement = pos - e->Position();
3482                 if ( displacement.NormSquared() > .01 )
3483                 {
3484                     dir = displacement;
3485                     dir.Normalize();
3486                 }
3487             }
3488             else
3489                 arena.LeastDangerousSpawnPoint()->Spawn( pos, dir );
3490 #ifdef DEBUG
3491             //                std::cout << "spawning player " << pni->name << '\n';
3492 #endif
3493             gCycle * cycle = new gCycle( grid, pos, dir, p );
3494             p->ControlObject(cycle);
3495 
3496             sg_Timestamp();
3497         }
3498     }
3499 }
3500 #endif
3501 
3502 static REAL sg_timestepMax = .2;
3503 static tSettingItem<REAL> sg_timestepMaxConf( "TIMESTEP_MAX", sg_timestepMax );
3504 static int sg_timestepMaxCount = 10;
3505 static tSettingItem<int> sg_timestepMaxCountConf( "TIMESTEP_MAX_COUNT", sg_timestepMaxCount );
3506 
Timestep(REAL time,bool cam)3507 void gGame::Timestep(REAL time,bool cam){
3508 #ifdef DEBUG
3509     tMemManBase::Check();
3510 #endif
3511 
3512 #ifdef RESPAWN_HACK
3513     // no respawining while deathzone is active.
3514     if( !winDeathZone_ )
3515     {
3516         sg_Respawn(time,grid,Arena);
3517     }
3518 #endif
3519 
3520     // chop timestep into small, managable bits
3521     REAL dt = time - lastTimeTimestep;
3522     if ( dt < 0 )
3523         return;
3524     REAL lt = lastTimeTimestep;
3525 
3526     // determine the number of bits
3527     int number_of_steps=int(fabs((dt)/sg_timestepMax));
3528     if (number_of_steps<1)
3529         number_of_steps=1;
3530     if ( number_of_steps > sg_timestepMaxCount )
3531     {
3532         number_of_steps = sg_timestepMaxCount;
3533     }
3534 
3535     // chop
3536     for(int i=1;i<=number_of_steps;i++)
3537     {
3538         REAL stepTime = lt + i * dt/number_of_steps;
3539         s_Timestep(grid, stepTime, cam);
3540     }
3541 }
3542 
3543 // check if team has any enemies currently
sg_EnemyExists(int team)3544 static bool sg_EnemyExists( int team )
3545 {
3546     // check if the win was legitimate: at least one enemy team needs to bo online
3547     for ( int i = eTeam::teams.Len()-1; i>= 0; --i )
3548     {
3549         if ( i != team && eTeam::Enemies( eTeam::teams[i], eTeam::teams[team] ) )
3550             return true;
3551     }
3552 
3553     return false;
3554 }
3555 
3556 static REAL sg_winZoneRandomness = .8;
3557 static tSettingItem< REAL > sg_winZoneSpreadConf( "WIN_ZONE_RANDOMNESS", sg_winZoneRandomness );
3558 
3559 static eLadderLogWriter sg_roundWinnerWriter("ROUND_WINNER", true);
3560 static eLadderLogWriter sg_matchWinnerWriter("MATCH_WINNER", true);
3561 
Analysis(REAL time)3562 void gGame::Analysis(REAL time){
3563     if ( nCLIENT == sn_GetNetState() )
3564         return;
3565 
3566     static REAL wintimer=0;
3567 
3568     // only do this expensive stuff once a second
3569     {
3570         static double nextTime = -1;
3571         if ( tSysTimeFloat() > nextTime || time < -10 )
3572         {
3573             nextTime = tSysTimeFloat() + 1.0;
3574         }
3575         else
3576         {
3577             return;
3578         }
3579     }
3580 
3581     // send timeout warnings
3582 
3583 
3584     if (tSysTimeFloat()-startTime>10){
3585         if ((startTime+sg_currentSettings->limitTime*60)-tSysTimeFloat()<10 && warning<6){
3586             tOutput o("$gamestate_tensecond_warn");
3587             sn_CenterMessage(o);
3588             sn_ConsoleOut(o);
3589             warning=6;
3590         }
3591 
3592 
3593         if ((startTime+sg_currentSettings->limitTime*60)-tSysTimeFloat()<30 && warning<5){
3594             tOutput o("$gamestate_30seconds_warn");
3595             sn_CenterMessage(o);
3596             sn_ConsoleOut(o);
3597             warning=5;
3598         }
3599 
3600         if ((startTime+sg_currentSettings->limitTime*60)-tSysTimeFloat()<60 && warning<4){
3601             tOutput o("$gamestate_minute_warn");
3602             sn_CenterMessage(o);
3603             sn_ConsoleOut(o);
3604             warning=4;
3605         }
3606 
3607         if ((startTime+sg_currentSettings->limitTime*60)-tSysTimeFloat()<2*60 && warning<3){
3608             tOutput o("$gamestate_2minutes_warn");
3609             sn_ConsoleOut(o);
3610             warning=3;
3611         }
3612 
3613         if ((startTime+sg_currentSettings->limitTime*60)-tSysTimeFloat()<5*60 && warning<2){
3614             tOutput o("$gamestate_5minutes_warn");
3615             sn_ConsoleOut(o);
3616             warning=2;
3617         }
3618 
3619         if ((startTime+sg_currentSettings->limitTime*60)-tSysTimeFloat()<10*60 && warning<1){
3620             tOutput o("$gamestate_10minutes_warn");
3621             sn_ConsoleOut(o);
3622             warning=1;
3623         }
3624     }
3625 
3626     // count active players
3627     int active = 0;
3628     int i;
3629     for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
3630         ePlayerNetID *pni=se_PlayerNetIDs(i);
3631         if (pni->IsActive())
3632             active ++;
3633     }
3634 
3635     // count other statistics
3636     int alive_and_not_disconnected = 0;
3637     int alive=0;
3638     int ai_alive=0;
3639     int human_teams=0;
3640     int teams_alive=0;
3641     // int last_alive=-1;
3642     int last_team_alive=-1;
3643     // int last_alive_and_not_disconnected=-1;
3644     int humans = 0;
3645     int active_humans = 0;
3646     int ais    = 0;
3647     REAL deathTime=0;
3648 
3649     bool notyetloggedin = true;  // are all current users not yet logged in?
3650 
3651     for(i=eTeam::teams.Len()-1;i>=0;i--){
3652         eTeam *t = eTeam::teams(i);
3653 
3654         humans += t->NumHumanPlayers();
3655         ais    += t->NumAIPlayers();
3656 
3657         if ( t->Alive() )
3658         {
3659             teams_alive++;
3660             last_team_alive = i;
3661         }
3662 
3663         if ( t->NumHumanPlayers() > 0 )
3664         { // human players
3665             human_teams++;
3666 
3667             for (int j=t->NumPlayers()-1; j>=0; --j)
3668             {
3669                 ePlayerNetID* p = t->Player(j);
3670                 if (p->IsActive())
3671                     active_humans++;
3672 
3673                 gCycle *g=dynamic_cast<gCycle *>(p->Object());
3674                 if(g){
3675                     notyetloggedin = false;
3676 
3677                     if(g->Alive())
3678                     {
3679 
3680                         if ( p->IsHuman() )
3681                         {
3682                             alive++;
3683                             if (p->IsActive())
3684                             {
3685                                 // last_alive_and_not_disconnected=i;
3686                                 alive_and_not_disconnected++;
3687                             }
3688                         }
3689                         else
3690                             ai_alive++;
3691 
3692                         // last_alive=i;
3693                     }
3694                     else
3695                     {
3696                         REAL dt=g->DeathTime();
3697                         if (dt>deathTime)
3698                             deathTime=dt;
3699                     }
3700                 }
3701 #ifdef KRAWALL_SERVER
3702                 else if (p->IsAuthenticated())
3703                     notyetloggedin = false;
3704 #endif
3705             }
3706         }
3707         else
3708         {
3709             for (int j=t->NumPlayers()-1; j>=0; --j)
3710             {
3711                 ePlayerNetID* p = t->Player(j);
3712 
3713                 gCycle *g=dynamic_cast<gCycle *>(p->Object());
3714                 if(g){
3715                     if(g->Alive())
3716                     {
3717                         ai_alive++;
3718                     }
3719                 }
3720             }
3721         }
3722     }
3723 
3724 #ifdef DEDICATED
3725     //activeHumans
3726     if (sg_NumUsers() <= 0)
3727         goon = false;
3728 #endif
3729 
3730     /*
3731     if (last_alive_and_not_disconnected >= 0)
3732         last_alive = last_alive_and_not_disconnected;
3733     */
3734 
3735     // kill all disconnected players if they are the only reason the round goes on:
3736     if ( alive_and_not_disconnected <= 1 && alive > alive_and_not_disconnected )
3737     {
3738         for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
3739         {
3740             gCycle *g=dynamic_cast<gCycle *>(se_PlayerNetIDs(i)->Object());
3741             if(g && !se_PlayerNetIDs(i)->IsActive())
3742             {
3743                 g->Kill();
3744             }
3745 
3746             //			alive = alive_and_not_disconnected;
3747         }
3748     }
3749 
3750     // keep the game running when there are only login processes running
3751     if (notyetloggedin && humans > 0 && ais == 0)
3752         alive++;
3753 
3754 
3755     static int all_alive_last=0;
3756     {
3757         int all_alive = ai_alive+alive;
3758         if (all_alive != all_alive_last)
3759         {
3760             all_alive_last = ai_alive+alive;
3761             lastdeath = time;
3762         }
3763     }
3764 
3765     static nVersionFeature winZone(2);
3766 
3767     // activate instant win zone
3768     if ( winZone.Supported() && !bool( winDeathZone_ ) && winner == 0 && time - lastdeath > sg_currentSettings->winZoneMinLastDeath && time > sg_currentSettings->winZoneMinRoundTime )
3769     {
3770         winDeathZone_ = sg_CreateWinDeathZone( grid, Arena.GetRandomPos( sg_winZoneRandomness ) );
3771     }
3772 
3773     bool holdBackNextRound = false;
3774 
3775     // start a new match if the number of teams changes from 1 to 2 or from 2 to 1
3776     {
3777         static int lastTeams = 0; // the number of teams when this was last called.
3778 
3779         // check for relevant status change, from 0 to 1 or 1 to 2 or 2 to 1 or 1 to 0 human teams.
3780         int humanTeamsClamp = human_teams;
3781         if ( humanTeamsClamp > 2 )
3782             humanTeamsClamp = 2;
3783         if ( humanTeamsClamp != lastTeams )
3784         {
3785             StartNewMatch();
3786             // if this is the beginning of a round, just start the match now
3787             if ( time < -100 )
3788             {
3789                 StartNewMatchNow();
3790             }
3791             else
3792             {
3793                 // start a new round and match.
3794                 winner=-1;
3795                 wintimer=time;
3796             }
3797         }
3798 
3799         lastTeams=humanTeamsClamp; // update last team count
3800     }
3801 
3802     // who is alive?
3803     if ( time-lastdeath < 1.0f )
3804     {
3805         holdBackNextRound = true;
3806     }
3807     else if (winner==0 && absolute_winner==0 ){
3808         if( teams_alive <= 1 )
3809         {
3810             if ( sg_currentSettings->gameType!=gFREESTYLE )
3811             {
3812                 if ( eTeam::teams.Len()>1 && teams_alive<=1 ){
3813                     winner=last_team_alive+1;
3814                     wintimer=time;
3815                 }
3816             }
3817             else
3818             {
3819                 if ( teams_alive < 1 )
3820                     winner=-1;
3821                 wintimer=time;
3822             }
3823         }
3824 
3825         const char* survivor="$player_win_survivor";
3826         const char* messagetype = survivor;
3827 
3828         if ( wishWinner )
3829         {
3830             wintimer=time;
3831             winner = wishWinner;
3832             wishWinner = 0;
3833             messagetype	= wishWinnerMessage;
3834         }
3835 
3836         if (winner <= 0 && alive <= 0 && ai_alive <= 0 && teams_alive <= 0){
3837             if ( se_mainGameTimer )
3838                 se_mainGameTimer->speed = 1;
3839 
3840             // emergency
3841             winner=-1;
3842             wintimer=time;
3843         }
3844         else
3845             if (winner){
3846                 if ( se_mainGameTimer )
3847                     se_mainGameTimer->speed = 1;
3848                 lastdeath = time;
3849 
3850                 if ( last_team_alive >= 0 )
3851                     sg_currentSettings->AutoAI( eTeam::teams( last_team_alive )->NumHumanPlayers() > 0 );
3852 
3853                 if ( sg_currentSettings->gameType!=gFREESTYLE && winner > 0 )
3854                 {
3855                     // check if the win was legitimate: at least one enemy team needs to bo online
3856                     if ( sg_EnemyExists( winner-1 ) )
3857                     {
3858 #ifdef KRAWALL_SERVER_LEAGUE
3859                         // send the result to the master server
3860                         if (!dynamic_cast<gAIPlayer*>(eTeam::teams(winner-1)))
3861                         {
3862                             tArray<tString> players;
3863                             for (int i = eTeam::teams.Len()-1; i>=0; i--)
3864                                 if (i != winner-1 &&
3865                                         eTeam::teams(i)->Alive() &&
3866                                         eTeam::teams(i)->IsHuman() )
3867                                 {
3868                                     players[players.Len()] = eTeam::teams(i)->Name();
3869                                 }
3870 
3871                             players[players.Len()] = eTeam::teams(winner-1)->Name();
3872                             if (players.Len() > 1)
3873                                 nKrawall::ServerRoundEnd(&players(0), players.Len());
3874                         }
3875 #endif
3876 
3877                         // scoring
3878                         eTeam::teams[winner-1]->AddScore
3879                         (sg_currentSettings->scoreWin,messagetype, tOutput());
3880                         if (!sg_singlePlayer &&
3881                                 eTeam::teams(winner-1)->NumHumanPlayers() >= 1 &&
3882                                 eTeam::teams(winner-1)->NumPlayers() >= 1 )
3883                         {
3884                             highscore_won_rounds.Add(eTeam::teams[winner-1] ,1);
3885                             highscore_ladder.winner(eTeam::teams(winner-1));
3886                         }
3887 
3888                         // print winning message
3889                         tOutput message;
3890                         message << "$gamestate_winner_winner";
3891                         message << eTeam::teams[winner-1]->Name();
3892                         sn_CenterMessage(message);
3893                         message << '\n';
3894                         se_SaveToScoreFile(message);
3895 
3896                         sg_roundWinnerWriter << ePlayerNetID::FilterName( eTeam::teams[winner-1]->Name() );
3897                         eTeam::WritePlayers( sg_roundWinnerWriter, eTeam::teams[winner-1] );
3898                         sg_roundWinnerWriter.write();
3899                     }
3900                 }
3901                 check_hs();
3902                 winner = -1;
3903             }
3904     }
3905 
3906     int winnerExtraRound = ( winner != 0 || alive == 0 ) ? 1 : 0;
3907 
3908 
3909     // do round end stuff one second after a winner was declared
3910     if ( winner && !roundOver && time-lastdeath >= 2.0f )
3911     {
3912         roundOver = true;
3913 
3914         const tList<eGameObject>& gameObjects = Grid()->GameObjects();
3915         for (int i=gameObjects.Len()-1;i>=0;i--)
3916         {
3917             eGameObject * e = gameObjects(i);
3918             if ( e )
3919             {
3920                 e->OnRoundEnd();
3921             }
3922         }
3923     }
3924 
3925     // analyze the ranking list
3926     if ( time-lastdeath < 2.0f )
3927     {
3928         holdBackNextRound = true;
3929     }
3930     else if (absolute_winner==0 &&
3931              sn_GetNetState()!=nCLIENT && se_PlayerNetIDs.Len()){
3932         ePlayerNetID::SortByScore();
3933         eTeam::SortByScore();
3934         if ( eTeam::teams.Len() > 0 )
3935             if (eTeam::teams.Len() <= 1
3936                     || ( eTeam::teams(0)->Score() > eTeam::teams(1)->Score() && sg_EnemyExists(0))){
3937 
3938                 // only then we can have a true winner:
3939                 if (eTeam::teams(0)->Score() >= sg_currentSettings->limitScore ||		// the score limit must be hit
3940                         rounds + winnerExtraRound >= sg_currentSettings->limitRounds ||     // or the round limit
3941                         tSysTimeFloat()>=startTime+sg_currentSettings->limitTime*60 ||      // or the time limit
3942                         (active <= 1 && eTeam::teams.Len() > 1)								// or all but one players must have disconnected.
3943                    )
3944                 {
3945                     bool declareChampion = true;
3946                     if ( winner!=0 && wintimer > time - ( sg_currentSettings->finishType==gFINISH_EXPRESS ? 2.0f : 4.0 ) )
3947                     {
3948                         declareChampion = false;
3949                         holdBackNextRound= true;
3950                     }
3951 
3952                     if ( declareChampion )
3953                     {
3954                         lastdeath = time + ( sg_currentSettings->finishType==gFINISH_EXPRESS ? 2.0f : 6.0f );
3955 
3956                         {
3957                             tOutput message;
3958                             message.SetTemplateParameter(1, eTeam::teams[0]->Name() );
3959                             message << "$gamestate_champ_center";
3960                             sn_CenterMessage(message);
3961                         }
3962 
3963                         tOutput message;
3964                         tColoredString name;
3965                         name << eTeam::teams[0]->Name();
3966                         name << tColoredString::ColorString(1,1,1);
3967 
3968                         sg_matchWinnerWriter << ePlayerNetID::FilterName( eTeam::teams[0]->Name() );
3969                         eTeam::WritePlayers( sg_matchWinnerWriter, eTeam::teams[0] );
3970                         sg_matchWinnerWriter.write();
3971 
3972                         message.SetTemplateParameter(1, name);
3973                         message << "$gamestate_champ_console";
3974 
3975                         if (eTeam::teams(0)->Score() >=sg_currentSettings->limitScore)
3976                         {
3977                             message.SetTemplateParameter(1, eTeam::teams(0)->Score());
3978                             message.SetTemplateParameter(2, sg_currentSettings->limitScore);
3979                             message << "$gamestate_champ_scorehit";
3980                         }
3981                         else if ( tSysTimeFloat()>=startTime+sg_currentSettings->limitTime*60)
3982                         {
3983                             message.SetTemplateParameter(1, sg_currentSettings->limitTime);
3984                             message << "$gamestate_champ_timehit";
3985                         }
3986                         else
3987                         {
3988                             message.SetTemplateParameter(1, rounds + 1);
3989                             message << "$gamestate_champ_default";
3990                         }
3991 
3992                         se_SaveToScoreFile(message);
3993                         se_SaveToScoreFile("$gamestate_champ_finalscores");
3994                         se_SaveToScoreFile(eTeam::Ranking( -1, false ));
3995                         se_SaveToScoreFile(ePlayerNetID::Ranking( -1, false ));
3996                         se_SaveToScoreFile(sg_GetCurrentTime( "Time: %Y/%m/%d %H:%M:%S\n" ));
3997                         se_SaveToScoreFile("\n\n");
3998 
3999                         eTeam* winningTeam = eTeam::teams(0);
4000                         if (!sg_singlePlayer && winningTeam->NumHumanPlayers() > 0 )
4001                         {
4002                             highscore_won_matches.Add( winningTeam, 1 );
4003                         }
4004 
4005                         sn_ConsoleOut(message);
4006                         wintimer=time;
4007                         absolute_winner=1;
4008 
4009                         if ( se_mainGameTimer )
4010                             se_mainGameTimer->speed = 1;
4011 
4012                         StartNewMatch();
4013                     }
4014                 }
4015             }
4016     }
4017 
4018 
4019     // time to wait after last death til we start a new round
4020     REAL fintime=6;
4021 
4022     if (sg_currentSettings->finishType==gFINISH_EXPRESS)
4023         fintime=2.5;
4024 
4025     if(( ( winner || absolute_winner ) && wintimer+fintime < time)
4026             || (((alive==0 && eTeam::teams.Len()>=
4027 #ifndef DEDICATED
4028                   1
4029 #else
4030                   0
4031 #endif
4032                  )) && time-lastdeath >fintime-2
4033 #ifndef DEDICATED
4034                 && humans > 0
4035 #endif
4036                )){
4037 #ifdef DEBUG
4038         //con << "winner=" << winner << ' ';
4039         //con << "wintimer=" << wintimer << ' ';
4040         //con << "time=" << time << ' ';
4041         //con << "dt=" << deathTime << '\n';
4042 #endif
4043         if (!holdBackNextRound &&
4044                 (
4045                     ( sg_currentSettings->finishType==gFINISH_IMMEDIATELY ||
4046                       sg_currentSettings->finishType==gFINISH_EXPRESS ||
4047                       absolute_winner || ( teams_alive <= 1 && time-lastdeath>4.0f ) ) ||
4048                     ( winner && time - wintimer> 4.0f )
4049                 )
4050            )
4051         {
4052             rounds++;
4053             SetState(GS_PLAY,GS_DELETE_OBJECTS);
4054         }
4055 
4056         if ( !winner && !absolute_winner && sg_currentSettings->finishType==gFINISH_SPEEDUP && se_mainGameTimer)
4057         {
4058             se_mainGameTimer->speed*=1.41;
4059             if(se_mainGameTimer->speed>16)
4060                 se_mainGameTimer->speed=16;
4061         }
4062 
4063     }
4064 }
4065 
4066 
StartNewMatch()4067 void gGame::StartNewMatch(){
4068     check_hs();
4069     rounds=-100;
4070 
4071 }
4072 
StartNewMatchNow()4073 void gGame::StartNewMatchNow(){
4074     if ( rounds != 0 )
4075         sg_newMatchWriter.write();
4076 
4077     rounds=0;
4078     warning=0;
4079     startTime=tSysTimeFloat()-10;
4080 
4081     ePlayerNetID::ThrowOutDisconnected();
4082     ePlayerNetID::ResetScore();
4083 }
4084 
4085 #ifdef DEBUG
4086 extern bool debug_grid;
4087 // from display.cpp
4088 bool simplify_grid=1;
4089 #endif
4090 
4091 #ifndef DEDICATED
4092 // mouse grab toggling
4093 static uActionGlobal togglemousegrab( "TOGGLE_MOUSEGRAB" );
togglemousegrab_func(REAL x)4094 static bool togglemousegrab_func(REAL x){
4095     if (x>0){
4096         su_mouseGrab = !su_mouseGrab;
4097     }
4098 
4099     return true;
4100 }
4101 static uActionGlobalFunc togglemousegrab_action(&togglemousegrab,&togglemousegrab_func, true );
4102 
4103 // pause game
4104 static uActionGlobal pausegame( "PAUSE_GAME" );
pausegame_func(REAL x)4105 static bool pausegame_func(REAL x){
4106     static bool paused=false;
4107 
4108     if (x>0){
4109         paused=!paused;
4110         se_PauseGameTimer(paused);
4111     }
4112 
4113     return true;
4114 }
4115 static uActionGlobalFunc pausegame_action(&pausegame,&pausegame_func, true );
4116 
4117 // texture reloading
4118 static uActionGlobal reloadtextures( "RELOAD_TEXTURES" );
reloadtextures_func(REAL x)4119 static bool reloadtextures_func(REAL x){
4120     if (x>0){
4121         rITexture::UnloadAll();
4122     }
4123 
4124     return true;
4125 }
4126 static uActionGlobalFunc reloadtextures_action(&reloadtextures,&reloadtextures_func, true );
4127 
4128 // menu
4129 static uActionGlobal ingamemenu( "INGAME_MENU" );
ingamemenu_func(REAL x)4130 static bool ingamemenu_func(REAL x){
4131     if (x>0){
4132         st_ToDo(&ingame_menu);
4133     }
4134 
4135     return true;
4136 }
4137 static uActionGlobalFunc ingamemenu_action(&ingamemenu,&ingamemenu_func, true );
4138 #endif // dedicated
4139 
4140 static eLadderLogWriter sg_gameTimeWriter("GAME_TIME", true);
4141 
GameLoop(bool input)4142 bool gGame::GameLoop(bool input){
4143     nNetState netstate = sn_GetNetState();
4144 
4145 #ifdef DEBUG
4146     grid->Check();
4147     if (simplify_grid)
4148 #endif
4149         grid->SimplifyAll(10);
4150 
4151 #ifdef DEBUG
4152     grid->Check();
4153 #endif
4154 
4155     //if (std::cin.good() && !std::cin.underflow())
4156     //tConfItemBase::LoadAll(std::cin);
4157     // network syncing
4158     REAL gtime=0;
4159     REAL time=0;
4160     se_SyncGameTimer();
4161     if (state==GS_PLAY){
4162         if ( netstate != sn_GetNetState() )
4163         {
4164             return false;
4165         }
4166         gtime=se_GameTime();
4167         time=gtime;
4168 
4169         if (sn_GetNetState()==nSTANDALONE && sg_IngameMenu)
4170             se_PauseGameTimer(true);
4171 
4172         static int lastcountdown=0;
4173         int cd=int(floor(-time))+1;
4174         if (cd>=0 && cd<PREPARE_TIME && cd!=lastcountdown && se_mainGameTimer && se_mainGameTimer->IsSynced() ){
4175             lastcountdown=cd;
4176             tString s;
4177             s << cd;
4178             con.CenterDisplay(s,0);
4179         }
4180     }
4181     //con << sg_netPlayerWalls.Len() << '\n';
4182 
4183 #ifndef DEDICATED
4184     if (input){
4185         su_HandleDelayedEvents();
4186 
4187         SDL_Event tEvent;
4188 
4189         uInputProcessGuard inputProcessGuard;
4190         while(su_GetSDLInput(tEvent,time)){
4191             if (time>gtime) time=gtime;
4192             if(time>lastTime_gameloop){
4193                 Timestep(time);
4194                 lastTime_gameloop=time;
4195             }
4196 
4197 
4198             if (!su_HandleEvent(tEvent, false))
4199                 switch (tEvent.type){
4200                 case SDL_MOUSEBUTTONDOWN:
4201                     break;
4202                 case SDL_KEYDOWN:
4203                     switch (tEvent.key.keysym.sym){
4204 
4205                     case(27):
4206                                     //                                case('q'):
4207                                     st_ToDo(&ingame_menu);
4208                         break;
4209 #ifdef DEBUG
4210                     case('d'):
4211                                     debug_grid=!debug_grid;
4212                         break;
4213 
4214                     case('a'):
4215                                     simplify_grid=!simplify_grid;
4216                         break;
4217 
4218                     case('l'):
4219                                     sr_glOut=!sr_glOut;
4220                         break;
4221 
4222                     case('b'):
4223                                     st_Breakpoint();
4224                         break;
4225 
4226                     case('t'):
4227                                     think=!think;
4228                         break;
4229                         /* #ifndef WIN32
4230                            case('f'):
4231                            fullscreen=(!fullscreen);
4232                            con << fullscreen << "x!\n";
4233                            XMesaSetFXmode(fullscreen ? XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW);
4234                            break;
4235                            #endif */
4236 #endif
4237 
4238                     default:
4239                         break;
4240                     }
4241                     break;
4242 
4243                 default:
4244                     break;
4245                 }
4246         }
4247 
4248         su_InputSync();
4249     }
4250 #endif
4251 
4252     if ( netstate != sn_GetNetState() )
4253     {
4254         return false;
4255     }
4256 
4257     // do basic network receiving and sending. This pushes out all input the player makes as fast as possible.
4258     sg_Receive();
4259     sn_SendPlanned();
4260 
4261     bool synced = se_mainGameTimer && ( se_mainGameTimer->IsSynced() || ( stateNext >= GS_DELETE_OBJECTS || stateNext <= GS_CREATE_GRID ) );
4262 
4263     if  (!synced)
4264     {
4265         // see if there is a game object owned by this client, if so, declare emergency sync
4266         for (int pi = MAX_PLAYERS-1; pi >= 0; --pi )
4267         {
4268             ePlayer * p = ePlayer::PlayerConfig(pi);
4269             if ( !p )
4270                 break;
4271             ePlayerNetID * np = p->netPlayer;
4272             if ( !np )
4273                 break;
4274             if ( np->Object() )
4275             {
4276                 synced = true;
4277                 break;
4278             }
4279         }
4280     }
4281     else if ( !synced_ )
4282     {
4283         // synced finally. Send our player info over so we can join the game.
4284         ePlayerNetID::Update();
4285 
4286         // and trigger the scheduled auto-logons.
4287         ePlayer::SendAuthNames();
4288 
4289         synced_ = true;
4290     }
4291 
4292     static float lastTime = 1e42;
4293 
4294     if(sg_gameTimeInterval >= 0 && (gtime >= lastTime + sg_gameTimeInterval || gtime < lastTime)) {
4295         sg_gameTimeWriter << gtime;
4296         sg_gameTimeWriter.write();
4297         lastTime = gtime;
4298     }
4299 
4300     if (state==GS_PLAY){
4301         if (gtime<0 && gtime>-PREPARE_TIME+.3)
4302             eCamera::s_Timestep(grid, gtime);
4303         else{
4304             // keep ping weight high while playing, that gives the best meassurements
4305             nPingAverager::SetWeight(1);
4306 
4307             if (gtime>=lastTime_gameloop){
4308 
4309  				#ifdef CONNECTION_STRESS
4310                 if ( sg_ConnectionStress )
4311                 {
4312                     tRandomizer & randomizer = tRandomizer::GetInstance();
4313                     int random = randomizer.Get( 1000 );
4314                     if ( random == 0 )
4315                     {
4316                         sg_RequestedDisconnection = true;
4317                         //						sn_SetNetState( nSTANDALONE );
4318                         goon = false;
4319                     }
4320                 }
4321 				#endif
4322 
4323 
4324                 Timestep(gtime,true);
4325                 lastTime_gameloop=gtime;
4326             }
4327             lastTime_gameloop=gtime;
4328         }
4329         //    else if (lastTime_gameloop>gtime+10)
4330         // lastTime_gameloop=gtime;
4331 
4332 
4333 
4334         if (sn_GetNetState()!=nCLIENT)
4335         {
4336             // simulate IAs
4337             for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
4338             {
4339                 gAIPlayer *ai = dynamic_cast<gAIPlayer*>(se_PlayerNetIDs(i));
4340                 if (ai && think)
4341                     ai->Timestep(gtime);
4342             }
4343 
4344             Analysis(gtime);
4345 
4346             // wait for chatting players
4347             if ( sn_GetNetState()==nSERVER && gtime < sg_lastChatBreakTime + 1 )
4348                 se_PauseGameTimer( gtime < sg_lastChatBreakTime && ePlayerNetID::WaitToLeaveChat() );
4349         }
4350 
4351         // send game object updates
4352         nNetObject::SyncAll();
4353         sn_SendPlanned();
4354 
4355         if ( gtime<=-PREPARE_TIME+.5 || !goon || !synced )
4356         {
4357 #ifndef DEDICATED
4358             if (input)
4359             {
4360                 if ( !synced )
4361                 {
4362                     con.CenterDisplay(tString(tOutput("$network_login_sync")),0);
4363                 }
4364 
4365                 if ( sr_glOut )
4366                     rSysDep::ClearGL();
4367             }
4368 
4369             if ( input )
4370                 rSysDep::SwapGL();
4371 #endif
4372         }
4373         else
4374             Render(grid, gtime, input);
4375 
4376         if ( netstate != sn_GetNetState() )
4377         {
4378             return false;
4379         }
4380 
4381         return goon;
4382     }
4383     else{
4384         // between rounds, assume we're synced
4385         //if ( !synced_ && sn_GetNetState() != nSERVER )
4386         //    ePlayerNetID::Update();
4387         // synced_ = true;
4388 
4389 #ifndef DEDICATED
4390         // send game object updates
4391         nNetObject::SyncAll();
4392         sn_SendPlanned();
4393 
4394         if (input)
4395         {
4396             if (sr_glOut)
4397                 rSysDep::ClearGL();
4398             rSysDep::SwapGL();
4399         }
4400 #endif
4401         tDelay( 10000 );
4402         return goon;
4403     }
4404 }
4405 
4406 
GridIsReady(int client)4407 bool gGame::GridIsReady(int client){
4408     return HasBeenTransmitted(client) &&
4409            (client_gamestate[client]>=GS_TRANSFER_OBJECTS &&
4410             client_gamestate[client]<=GS_DELETE_OBJECTS);
4411 }
4412 
4413 #include <fcntl.h>
4414 #include <stdio.h>
4415 #include <errno.h>
4416 
GameLoop(bool input=true)4417 bool GameLoop(bool input=true){
4418     /*
4419       int oldflags = fcntl (fileno(stdin), F_GETFD, 0);
4420       if (oldflags<0)
4421       std::cout << errno << '\n';
4422 
4423       if (fcntl(fileno(stdin), F_SETFL, oldflags | O_NONBLOCK)<0)
4424       std::cout << errno << '\n';
4425 
4426     //if (std::cin && std::cin.good() && !std::cin.eof() && !std::cin.fail() && !std::cin.bad()){
4427     //if (std::cin.rdbuf()->overflow()!=EOF){
4428     //if (std::cin.rdbuf()->in_avail()>0){
4429 
4430     char c[10];
4431     int in=0;
4432     in=read (fileno(stdin),c,9);
4433     for (int i=0;i<in;i++)
4434     std::cout << c;
4435      */
4436 
4437     // see if pings have dramatically changed
4438     if(sn_GetNetState()==nSERVER){
4439         REAL t=tSysTimeFloat();
4440         for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--){
4441             ePlayerNetID *p=se_PlayerNetIDs(i);
4442 
4443             // get average and variance (increased a bit) of this player's ping
4444             nPingAverager const & averager = sn_Connections[p->Owner()].ping;
4445             REAL realping = averager.GetPing();
4446             REAL pingvariance = averager.GetSlowAverager().GetDataVariance() + realping * realping * .01;
4447 
4448             // deviation of real ping and synced ping
4449             REAL deviation = realping - p->ping;
4450 
4451             // measured against the variance, this player's ping deviates this much
4452             // from the communicated value
4453             REAL relativedeviation = deviation * deviation / pingvariance;
4454 
4455             if (0 == p->Owner())
4456                 realping = 0;
4457 
4458             if ( ( t - p->lastSync - 5 ) * relativedeviation > 5 ){
4459                 // if yes, send a message about it
4460                 p->ping = realping;
4461                 p->RequestSync(false);
4462             }
4463         }
4464     }
4465 
4466     bool goon=false;
4467     if(sg_currentGame){
4468         tControlledPTR< nNetObject > keep( sg_currentGame );
4469         sg_currentGame->StateUpdate();
4470         goon=!uMenu::quickexit && bool(sg_currentGame) && sg_currentGame->GameLoop(input);
4471     }
4472     return goon;
4473 }
4474 
gameloop_idle()4475 void gameloop_idle()
4476 {
4477     se_UserShowScores( false );
4478     sg_Receive();
4479     nNetObject::SyncAll();
4480     sn_SendPlanned();
4481     GameLoop(false);
4482 }
4483 
4484 static eSoundPlayer introPlayer(intro);
4485 static eSoundPlayer extroPlayer(extro);
4486 
4487 static void sg_EnterGameCleanup();
4488 
sg_EnterGameCore(nNetState enter_state)4489 void sg_EnterGameCore( nNetState enter_state ){
4490     sg_RequestedDisconnection = false;
4491 
4492     sr_con.SetHeight(7);
4493 
4494     extroPlayer.End();
4495     introPlayer.MakeGlobal();
4496     introPlayer.Reset();
4497 
4498     sg_SoundPause( true, false );
4499 
4500     //  exit_game_objects(grid);
4501 
4502     // enter single player settings
4503     if ( sn_GetNetState() != nCLIENT )
4504     {
4505         sg_currentSettings = &singlePlayer;
4506         sg_copySettings();
4507     }
4508 
4509     gHighscoresBase::LoadAll();
4510 
4511     uMenu::SetIdle(gameloop_idle);
4512     sr_con.autoDisplayAtSwap=true;
4513     bool goon=true;
4514     while (bool(sg_currentGame) && goon && sn_GetNetState()==enter_state){
4515 #ifdef DEDICATED // read input
4516         sr_Read_stdin();
4517         if ( sn_BasicNetworkSystem.Select( 1.0 / ( sg_dedicatedFPSIdleFactor * sg_dedicatedFPS )  ) )
4518         {
4519             // new network data arrived, do the most urgent work now
4520             tAdvanceFrame();
4521             sg_Receive();
4522             se_SyncGameTimer();
4523             REAL time=se_GameTime();
4524             sg_currentGame->StateUpdate();
4525             if ( time > 0 )
4526             {
4527                 // only simulate the objects that have pending events to execute
4528                 eGameObject::s_Timestep(sg_currentGame->Grid(), time, 1E+10 );
4529 
4530                 // send out updates immediately
4531                 nNetObject::SyncAll();
4532                 tAdvanceFrame();
4533                 sn_SendPlanned();
4534             }
4535         }
4536 #endif
4537 
4538         // do the regular simulation
4539         tAdvanceFrame();
4540 
4541         goon=GameLoop();
4542 
4543         st_DoToDo();
4544     }
4545 
4546     sg_SoundPause( false, false );
4547 
4548     extroPlayer.MakeGlobal();
4549     extroPlayer.Reset();
4550 
4551     sg_EnterGameCleanup();
4552 }
4553 
sg_EnterGameCleanup()4554 void sg_EnterGameCleanup()
4555 {
4556     gHighscoresBase::SaveAll();
4557 
4558     sn_SetNetState( nSTANDALONE );
4559 
4560     // delete all AI players
4561     gAIPlayer::ClearAll();
4562     // gAIPlayer::SetNumberOfAIs(0, 0, 0, -1);
4563     // gAITeam::BalanceWithAIs( false );
4564     // gAIPlayer::ClearAll();
4565 
4566     gLogo::SetDisplayed(true);
4567     uMenu::SetIdle(NULL);
4568     sr_con.autoDisplayAtSwap=false;
4569     sr_con.fullscreen=false;
4570 
4571     //  exit_game_objects(grid);
4572     nNetObject::ClearAll();
4573     ePlayerNetID::ClearAll();
4574     sg_currentGame = NULL;
4575     uMenu::exitToMain = false;
4576 
4577     rModel::ClearCache();
4578 }
4579 
sg_EnterGame(nNetState enter_state)4580 void sg_EnterGame( nNetState enter_state )
4581 {
4582     try
4583     {
4584         // enter the game
4585         sg_EnterGameCore( enter_state );
4586     }
4587     catch (tException const & e)
4588     {
4589         // cleanup
4590         sg_EnterGameCleanup();
4591 
4592         // inform user of generic errors
4593         tConsole::Message( e.GetName(), e.GetDescription(), 120000 );
4594     }
4595     // again: VC6 does not catch tGenericException with above statement
4596 #ifdef _MSC_VER
4597 #pragma warning ( disable : 4286 )
4598     catch (tGenericException const & e)
4599     {
4600         // cleanup
4601         sg_EnterGameCleanup();
4602 
4603         // inform user of generic errors
4604         tConsole::Message( e.GetName(), e.GetDescription(), 120000 );
4605     }
4606 #endif
4607 
4608     // bounce teams
4609     for ( int i = eTeam::teams.Len()-1; i>=0; --i )
4610     {
4611         tJUST_CONTROLLED_PTR< eTeam > t = eTeam::teams(i);
4612     }
4613 }
4614 
GridIsReady(int c)4615 bool GridIsReady(int c){
4616     return bool(sg_currentGame) && sg_currentGame->GridIsReady(c);
4617 }
4618 
4619 
4620 // avoid transfer of game objects if the grid is not yes constructed
notrans()4621 static bool notrans(){
4622     return !GridIsReady(eTransferInhibitor::User());
4623 }
4624 
4625 static eTransferInhibitor inh(&notrans);
4626 
4627 // are we active?
Activate(bool act)4628 void Activate(bool act){
4629 #ifdef DEBUG
4630     return;
4631 #endif
4632 
4633 // another fullscreen fix ammendmend: avoid the short screen flicker
4634 // by ignoring deactivation events in fullscreen mode completely.
4635 #ifndef WIN32
4636 #ifndef MACOSX
4637     if ( currentScreensetting.fullscreen && !act )
4638     {
4639         return;
4640     }
4641 #endif
4642 #endif
4643 
4644     sr_Activate( act );
4645 
4646     sg_SoundPause( !act, true );
4647 
4648     if ( !tRecorder::IsRunning() )
4649     {
4650         if (sn_GetNetState()==nSTANDALONE)
4651         {
4652             se_PauseGameTimer(!act);
4653         }
4654 
4655         se_ChatState( ePlayerNetID::ChatFlags_Away, !act);
4656     }
4657 }
4658 
4659 // ************************************
4660 // full screen messages from the server
4661 // ************************************
4662 
4663 static nVersionFeature sg_fullscreenMessages(14);
4664 
sg_FullscreenIdle()4665 static void sg_FullscreenIdle()
4666 {
4667     tAdvanceFrame();
4668     se_SyncGameTimer();
4669     gGame::NetSync();
4670     if ( sg_currentGame )
4671         sg_currentGame->StateUpdate();
4672 }
4673 
sg_ClientFullscreenMessage(tOutput const & title,tOutput const & message,REAL timeout)4674 void sg_ClientFullscreenMessage( tOutput const & title, tOutput const & message, REAL timeout ){
4675     // keep syncing the network
4676     rPerFrameTask idle( sg_FullscreenIdle );
4677 
4678     // put players into idle mode
4679     ePlayerNetID::SpectateAll();
4680     se_ChatState( ePlayerNetID::ChatFlags_Menu, true );
4681 
4682     // show message
4683     uMenu::Message( title, message, timeout );
4684 
4685     // and print it to the console
4686 #ifndef DEDICATED
4687     con <<  title << "\n" << message << "\n";
4688 #endif
4689 
4690     // get players out of idle mode again
4691     ePlayerNetID::SpectateAll(false);
4692     se_ChatState( ePlayerNetID::ChatFlags_Menu, false );
4693 }
4694 
4695 static tString sg_fullscreenMessageTitle;
4696 static tString sg_fullscreenMessageMessage;
4697 static REAL sg_fullscreenMessageTimeout;
sg_TodoClientFullscreenMessage()4698 static void sg_TodoClientFullscreenMessage()
4699 {
4700     sg_ClientFullscreenMessage( sg_fullscreenMessageTitle, sg_fullscreenMessageMessage, sg_fullscreenMessageTimeout );
4701 }
4702 
sg_ClientFullscreenMessage(nMessage & m)4703 static void sg_ClientFullscreenMessage(nMessage &m){
4704     if (sn_GetNetState()!=nSERVER){
4705         // to test timeouts during fullscreen message display:
4706         // m.Send( m.SenderID() );
4707 
4708         sg_fullscreenMessageTimeout = 60;
4709 
4710         m >> sg_fullscreenMessageTitle;
4711         m >> sg_fullscreenMessageMessage;
4712         m >> sg_fullscreenMessageTimeout;
4713 
4714         st_ToDo( sg_TodoClientFullscreenMessage );
4715     }
4716 }
4717 
4718 static nDescriptor sg_clientFullscreenMessage(312,sg_ClientFullscreenMessage,"client_fsm");
4719 
4720 // causes the connected clients to break and print a fullscreen message
sg_FullscreenMessage(tOutput const & title,tOutput const & message,REAL timeout,int client)4721 void sg_FullscreenMessage(tOutput const & title, tOutput const & message, REAL timeout, int client){
4722     tJUST_CONTROLLED_PTR< nMessage > m=new nMessage(sg_clientFullscreenMessage);
4723     *m << title;
4724     *m << message;
4725     *m << timeout;
4726 
4727     tString complete( title );
4728     complete << "\n" << message << "\n";
4729 
4730     if (client <= 0){
4731         if ( sg_fullscreenMessages.Supported() )
4732             m->BroadCast();
4733         else
4734         {
4735             for(int c = MAXCLIENTS; c > 0; --c)
4736             {
4737                 if ( sn_Connections[c].socket )
4738                 {
4739                     if ( sg_fullscreenMessages.Supported(c) )
4740                         m->Send(c);
4741                     else
4742                         sn_ConsoleOut(complete, c);
4743                 }
4744             }
4745         }
4746 
4747         // display the message locally, waiting for the clients to have seen it
4748         {
4749             // stop the game
4750             bool paused = se_mainGameTimer && se_mainGameTimer->speed < .0001;
4751             se_PauseGameTimer(true);
4752             gGame::NetSyncIdle();
4753 
4754             REAL waitTo = tSysTimeFloat() + timeout;
4755             REAL waitToMin = tSysTimeFloat() + 1.0;
4756             sg_ClientFullscreenMessage( title, message, timeout );
4757 
4758             // wait for players to see it
4759             bool goon = true;
4760             while( goon && waitTo > tSysTimeFloat() )
4761             {
4762                 sg_FullscreenIdle();
4763                 gameloop_idle();
4764                 if ( se_GameTime() > sg_lastChatBreakTime )
4765                     se_PauseGameTimer(true);
4766 
4767                 // give the clients a second to enter chat state
4768                 if ( tSysTimeFloat() > waitToMin )
4769                 {
4770                     goon = false;
4771                     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
4772                     {
4773                         ePlayerNetID* player = se_PlayerNetIDs(i);
4774                         if ( player->IsChatting() )
4775                             goon = true;
4776                     }
4777                 }
4778             }
4779 
4780             // continue the game
4781             se_PauseGameTimer(paused);
4782             gGame::NetSyncIdle();
4783         }
4784     }
4785     else
4786     {
4787         if ( sg_fullscreenMessages.Supported(client) )
4788             m->Send(client);
4789         else
4790             sn_ConsoleOut(complete, client);
4791     }
4792 }
4793 
sg_FullscreenMessageConf(std::istream & s)4794 static void sg_FullscreenMessageConf(std::istream &s)
4795 {
4796     // read the timeout
4797     REAL timeout;
4798     s >> timeout;
4799 
4800     if ( !s.good() || s.eof() )
4801     {
4802         con << "Usage: FULLSCREEN_MESSAGE <timeout> <message>\n";
4803         return;
4804     }
4805 
4806     // read the message
4807     tString message;
4808     message.ReadLine( s, true );
4809 
4810     message += "\n";
4811 
4812     // display it
4813     sg_FullscreenMessage( "$fullscreen_message_title", message , timeout );
4814 }
4815 
4816 static tConfItemFunc sg_fullscreenMessageConf("FULLSCREEN_MESSAGE",&sg_FullscreenMessageConf);
4817 
4818 // **************
4819 // Login callback
4820 // **************
4821 
4822 // message of day presented to clients logging in
4823 tString sg_greeting("");
4824 static tConfItemLine a_mod("MESSAGE_OF_DAY",sg_greeting);
4825 
4826 tString sg_greetingTitle("");
4827 static tConfItemLine a_tod("TITLE_OF_DAY",sg_greetingTitle);
4828 
4829 REAL sg_greetingTimeout=60;
4830 static tSettingItem< REAL > a_modt("MESSAGE_OF_DAY_TIMEOUT",sg_greetingTimeout);
4831 
LoginCallback()4832 static void LoginCallback(){
4833     client_gamestate[nCallbackLoginLogout::User()]=0;
4834     if ( nCallbackLoginLogout::Login() && nCallbackLoginLogout::User() > 0 )
4835     {
4836         if ( sg_greeting.Len()>1 )
4837         {
4838             if ( sg_greetingTitle.Len() <= 1 )
4839                 sg_greetingTitle = "Server message";
4840 
4841             sg_FullscreenMessage( sg_greetingTitle, sg_greeting, sg_greetingTimeout, nCallbackLoginLogout::User() );
4842         }
4843     }
4844 
4845     // flag indicating wether we were in client bode
4846     static bool wasClient = false;
4847 
4848     // lost connection to server/disconnected
4849     if( wasClient && !nCallbackLoginLogout::Login() && sn_GetNetState() == nSTANDALONE )
4850     {
4851         // drop all menu activity
4852         if ( !uMenu::quickexit )
4853         {
4854             uMenu::quickexit = uMenu::QuickExit_Game;
4855         }
4856     }
4857 
4858     wasClient = nCallbackLoginLogout::Login() && nCallbackLoginLogout::User() == 0;
4859 }
4860 
4861 static nCallbackLoginLogout lc(LoginCallback);
4862 
4863 
4864