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(¬rans);
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