1 #ifndef CLIENT_H_INCLUDED 2 #define CLIENT_H_INCLUDED 3 4 #include <string> 5 #include <sstream> 6 #include <iomanip> 7 #include "base.h" 8 #include "Exception.h" 9 #include "Log.h" 10 #include "Player.h" 11 #include "storage.h" 12 #include "Networking.h" 13 #include "Settings.h" 14 #include "clientversion.h" 15 16 #include "ContentDownloader.h" 17 #include "SheepGenerator.h" 18 #include "Shepherd.h" 19 #include "Voting.h" 20 #include "Timer.h" 21 #include "TextureFlat.h" 22 #include "Hud.h" 23 #include "Splash.h" 24 #include "StatsConsole.h" 25 #include "ServerMessage.h" 26 #include "Matrix.h" 27 #include "CrossFade.h" 28 #include "StartupScreen.h" 29 #if defined(WIN32) && defined(_MSC_VER) 30 #include "../msvc/msvc_fix.h" 31 #include "../msvc/cpu_usage_win32.h" 32 #else 33 #if defined(LINUX_GNU) 34 #include <limits.h> 35 #include "cpu_usage_linux.h" 36 #else 37 #include "cpu_usage_mac.h" 38 #endif 39 #endif 40 /* 41 CElectricSheep(). 42 Prime mover for the client, used from main.cpp... 43 */ 44 class CElectricSheep 45 { 46 protected: 47 ESCpuUsage m_CpuUsage; 48 fp8 m_LastCPUCheckTime; 49 int m_CpuUsageTotal; 50 int m_CpuUsageES; 51 int m_HighCpuUsageCounter; 52 int m_CpuUsageThreshold; 53 bool m_MultipleInstancesMode; 54 bool m_bConfigMode; 55 bool m_SeamlessPlayback; 56 Base::CTimer m_Timer; 57 Base::CTimer m_F1F4Timer; 58 59 Hud::spCHudManager m_HudManager; 60 Hud::spCStartupScreen m_StartupScreen; 61 62 fp8 m_PNGDelayTimer; 63 64 // The base framerate(from config). 65 fp8 m_PlayerFps; 66 67 // Potentially adjusted framerate(from <- and -> keys) 68 fp8 m_CurrentFps; 69 70 fp8 m_OriginalFps; 71 72 // Voting object. 73 CVote *m_pVoter; 74 75 // Default root directory, ie application data. 76 std::string m_AppData; 77 78 // Default application working directory. 79 std::string m_WorkingDir; 80 81 82 // Splash images. 83 Hud::spCSplash m_spSplashPos; 84 Hud::spCSplash m_spSplashNeg; 85 86 // Splash PNG 87 Hud::spCSplashImage m_spSplashPNG; 88 Base::CTimer m_SplashPNGDelayTimer; 89 int m_nSplashes; 90 std::string m_SplashFilename; 91 92 Hud::spCCrossFade m_spCrossFade; 93 94 std::deque<std::string> m_ConnectionErrors; 95 96 uint32 m_curPlayingID; 97 uint32 m_curPlayingGen; 98 uint64 m_lastPlayedSeconds; 99 100 #ifdef DO_THREAD_UPDATE 101 boost::barrier* m_pUpdateBarrier; 102 103 boost::thread_group *m_pUpdateThreads; 104 105 boost::mutex m_BarrierMutex; 106 #endif 107 108 109 // Init tuplestorage. 110 bool InitStorage(bool _bReadOnly = false) 111 { 112 g_Log->Info( "InitStorage()" ); 113 #ifndef LINUX_GNU 114 if (g_Settings()->Init( m_AppData, m_WorkingDir, _bReadOnly ) == false) 115 #else 116 char appdata[PATH_MAX]; 117 snprintf( appdata, PATH_MAX, "%s/.electricsheep/", getenv("HOME") ); 118 if (g_Settings()->Init( appdata, SHAREDIR ) == false) 119 #endif 120 return false; 121 122 // Trigger this to exist in the settings. 123 #ifndef LINUX_GNU 124 g_Settings()->Set( "settings.app.InstallDir", m_WorkingDir ); 125 #else 126 g_Settings()->Set( "settings.app.InstallDir", SHAREDIR ); 127 #endif 128 return true; 129 } 130 131 // Attach log. AttachLog()132 void AttachLog() 133 { 134 TupleStorage::IStorageInterface::CreateFullDirectory( m_AppData + "Logs/" ); 135 if (g_Settings()->Get( "settings.app.log", false )) 136 g_Log->Attach( m_AppData + "Logs/" ); 137 g_Log->Info( "AttachLog()" ); 138 139 g_Log->Info( "******************* %s (Built %s / %s)...\n", CLIENT_VERSION_PRETTY, __DATE__,__TIME__ ); 140 } 141 142 public: CElectricSheep()143 CElectricSheep() 144 { 145 m_CpuUsageTotal = -1; 146 m_CpuUsageES = -1; 147 m_CpuUsageThreshold = 50; 148 m_HighCpuUsageCounter = 0; 149 m_bConfigMode = false; 150 m_MultipleInstancesMode = false; 151 printf( "CElectricSheep()\n" ); 152 153 m_pVoter = NULL; 154 #ifndef LINUX_GNU 155 m_AppData = "./.ElectricSheep/"; 156 m_WorkingDir = "./"; 157 #else 158 m_AppData = std::string(getenv("HOME"))+"/.electricsheep/"; 159 m_WorkingDir = SHAREDIR; 160 #endif 161 #ifdef DO_THREAD_UPDATE 162 m_pUpdateBarrier = NULL; 163 m_pUpdateThreads = NULL; 164 #endif 165 } 166 ~CElectricSheep()167 virtual ~CElectricSheep() 168 { 169 } 170 171 // Startup()172 virtual bool Startup() 173 { 174 m_CpuUsageThreshold = g_Settings()->Get( "settings.player.cpuusagethreshold", 50 ); 175 176 if (m_MultipleInstancesMode == false) 177 { 178 g_NetworkManager->Startup(); 179 180 // Set proxy info. 181 if( g_Settings()->Get( "settings.content.use_proxy", false ) ) 182 { 183 g_Log->Info( "Using proxy server..." ); 184 g_NetworkManager->Proxy( g_Settings()->Get( "settings.content.proxy", std::string("") ), 185 g_Settings()->Get( "settings.content.proxy_username", std::string("") ), 186 g_Settings()->Get( "settings.content.proxy_password", std::string("") ) ); 187 } 188 189 //if some user has a legacy clear-text password, lets convert it to md5 one. (should we remove the old one ???) 190 if (g_Settings()->Get( "settings.content.password_md5", std::string("") ) == "") { 191 g_Settings()->Set( "settings.content.password_md5", ContentDownloader::Shepherd::computeMD5( g_Settings()->Get( "settings.content.password", std::string("") ) ) ); 192 //g_Settings()->Set( "settings.content.password", "" ); 193 } 194 195 g_NetworkManager->Login( g_Settings()->Get( "settings.generator.nickname", std::string("") ), g_Settings()->Get( "settings.content.password_md5", std::string("") ) ); 196 197 m_pVoter = new CVote(); 198 } 199 200 g_Player().SetMultiDisplayMode( (CPlayer::MultiDisplayMode)g_Settings()->Get( "settings.player.MultiDisplayMode", 0 ) ); 201 202 // Init the display and create decoder. 203 if( !g_Player().Startup() ) 204 return false; 205 206 #ifdef DO_THREAD_UPDATE 207 CreateUpdateThreads(); 208 #endif 209 210 m_curPlayingID = 0; 211 m_curPlayingGen = 0; 212 m_lastPlayedSeconds = 0; 213 214 // Set framerate. 215 m_PlayerFps = g_Settings()->Get( "settings.player.player_fps", 20. ); 216 if ( m_PlayerFps < 0.1 ) 217 m_PlayerFps = 1.; 218 m_OriginalFps = m_PlayerFps; 219 m_CurrentFps = m_PlayerFps; 220 221 g_Player().Framerate( m_CurrentFps ); 222 223 224 // Get hud font size. 225 std::string hudFontName = g_Settings()->Get( "settings.player.hudFontName", std::string("Trebuchet MS") ); 226 uint32 hudFontSize = static_cast<uint32>(g_Settings()->Get( "settings.player.hudFontSize", 24 )); 227 228 m_PNGDelayTimer = g_Settings()->Get( "settings.player.pngdelaytimer", 600); 229 230 m_HudManager = new Hud::CHudManager; 231 232 m_HudManager->Add( "helpmessage", new Hud::CStatsConsole( Base::Math::CRect( 1, 1 ), hudFontName, hudFontSize ) ); 233 234 Hud::spCStatsConsole spHelpMessage = (Hud::spCStatsConsole)m_HudManager->Get( "helpmessage" ); 235 spHelpMessage->Add( new Hud::CStringStat( "message", "Electric Sheep\n\nYou are now part of a cyborg mind composed of thousands of\ncomputers and people communicating with a genetic algorithm.\n\nKeyboard Commands\nUp-arrow: vote for this sheep\nDown-arrow: vote against this sheep and delete it\nLeft-arrow: go back to play previous sheep\nRight-arrow: go forward through history\n" 236 #ifdef MAC 237 "Cmd" 238 #else 239 "Control" 240 #endif 241 "-F: go full screen (if in window)\nF1: help (this page)\nF2: download status and error log\nF3: render status\nF4: playback status\n\nby Scott Draves and open source programmers all over the world\nElectricSheep.org", "" ) ); 242 243 std::string ver = GetVersion(); 244 245 if (!ver.empty()) 246 { 247 spHelpMessage->Add( new Hud::CStringStat( "version", "\nVersion: ", ver ) ); 248 } 249 250 m_SeamlessPlayback = g_Settings()->Get( "settings.player.SeamlessPlayback", false ); 251 252 // Add some display stats. 253 m_HudManager->Add( "displaystats", new Hud::CStatsConsole( Base::Math::CRect( 1, 1 ), hudFontName, hudFontSize ) ); 254 Hud::spCStatsConsole spStats = (Hud::spCStatsConsole)m_HudManager->Get( "displaystats" ); 255 spStats->Add( new Hud::CStringStat( "decodefps", "Decoding video at ", "? fps" ) ); 256 257 258 int32 displayMode = g_Settings()->Get( "settings.player.DisplayMode", 0 ); 259 if( displayMode == 2 ) 260 spStats->Add( new Hud::CAverageCounter( "displayfps", 261 g_Player().Renderer()->Description() + 262 " piecewise cubic display at ", " fps", 1.0 ) ); 263 else if( displayMode == 1 ) 264 spStats->Add( new Hud::CAverageCounter( "displayfps", 265 g_Player().Renderer()->Description() + 266 " piecewise linear display at ", " fps", 1.0 ) ); 267 else 268 spStats->Add( new Hud::CAverageCounter( "displayfps", 269 g_Player().Renderer()->Description() + 270 " display at ", " fps", 1.0 ) ); 271 272 spStats->Add( new Hud::CStringStat( "currentid", "Currently playing sheep: ", "n/a" ) ); 273 spStats->Add( new Hud::CStringStat( "uptime", "\nClient uptime: ", "...." ) ); 274 275 // Add some server stats. 276 m_HudManager->Add( "serverstats", new Hud::CStatsConsole( Base::Math::CRect( 1, 1 ), hudFontName, hudFontSize ) ); 277 spStats = (Hud::spCStatsConsole)m_HudManager->Get( "serverstats" ); 278 spStats->Add( new Hud::CStringStat( "loginstatus", "", "Not logged in" ) ); 279 spStats->Add( new Hud::CStringStat( "all", "Local flock: ", "unknown..." ) ); 280 spStats->Add( new Hud::CStringStat( "server", "Server is ", "not known yet" ) ); 281 spStats->Add( new Hud::CStringStat( "transfers", "", "" ) ); 282 spStats->Add( new Hud::CStringStat( "deleted", "", "" ) ); 283 spStats->Add( new Hud::CStringStat( "bsurvivors", "", "" ) ); 284 if (m_MultipleInstancesMode == true) 285 spStats->Add( new Hud::CTimeCountDownStat( "svstat", "", "Downloading disabled, read-only mode" ) ); 286 else 287 if (g_Settings()->Get( "settings.content.download_mode", true ) == false) 288 spStats->Add( new Hud::CTimeCountDownStat( "svstat", "", "Downloading disabled" ) ); 289 else 290 spStats->Add( new Hud::CTimeCountDownStat( "svstat", "", "Preparing downloader..." ) ); 291 spStats->Add( new Hud::CStringStat( std::string("zconnerror"), "", "" ) ); 292 293 // Add some display stats. 294 m_HudManager->Add( "renderstats", new Hud::CStatsConsole( Base::Math::CRect( 1, 1 ), hudFontName, hudFontSize ) ); 295 spStats = (Hud::spCStatsConsole)m_HudManager->Get( "renderstats" ); 296 spStats->Add( new Hud::CTimeCountDownStat( "countdown", "", "Rendering disabled" ) ); 297 spStats->Add( new Hud::CIntCounter( "rendering", "Currently rendering ", " frames" ) ); 298 spStats->Add( new Hud::CIntCounter( "totalframes", "", " frames rendered" ) ); 299 300 bool hasBattery = (GetACLineStatus() != -1); 301 302 if (hasBattery) 303 spStats->Add( new Hud::CStringStat( "zbattery", "\nPower source: ", "Unknown" ) ); 304 305 spStats->Add( new Hud::CStringStat( "zzacpu", "CPU usage: ", "Unknown" ) ); 306 307 #ifndef LINUX_GNU 308 std::string defaultDir = std::string(".\\"); 309 #else 310 std::string defaultDir = std::string(""); 311 #endif 312 // Vote splash. 313 m_spSplashPos = new Hud::CSplash( 0.2f, g_Settings()->Get( "settings.app.InstallDir", defaultDir ) + "electricsheep-smile.png" ); 314 m_spSplashNeg = new Hud::CSplash( 0.2f, g_Settings()->Get( "settings.app.InstallDir", defaultDir ) + "electricsheep-frown.png" ); 315 316 // PNG splash 317 m_SplashFilename = g_Settings()->Get( "settings.player.attrpngfilename", g_Settings()->Get( "settings.app.InstallDir", defaultDir ) + "electricsheep-attr.png" ); 318 319 bool splashFound = false; 320 321 const char *percent; 322 323 if ( m_SplashFilename.empty() == false ) 324 { 325 if (( percent = strchr(m_SplashFilename.c_str(), '%') )) 326 { 327 if (percent[1] == 'd') 328 { 329 FILE *test; 330 while (1) 331 { 332 char fNameFormatted[FILENAME_MAX]; 333 snprintf(fNameFormatted, FILENAME_MAX, m_SplashFilename.c_str(), m_nSplashes); 334 if ((test = fopen(fNameFormatted, "r"))) 335 { 336 splashFound = true; 337 fclose(test); 338 m_nSplashes++; 339 } 340 else 341 { 342 break; 343 } 344 } 345 } 346 } 347 else 348 { 349 FILE *test = fopen( m_SplashFilename.c_str(), "r" ); 350 351 if ( test != NULL ) 352 { 353 splashFound = true; 354 fclose( test ); 355 } 356 } 357 } 358 359 360 if ( !splashFound ) 361 { 362 m_SplashFilename = g_Settings()->Get( "settings.app.InstallDir", defaultDir ) + "electricsheep-attr.png"; 363 g_Settings()->Set( "settings.player.attrpngfilename", m_SplashFilename ); 364 } 365 366 367 // if multiple splashes are found then they are loaded when the timer goes off, not here 368 if ( m_SplashFilename.empty() == false && g_Settings()->Get( "settings.app.attributionpng", true ) == true ) 369 m_spSplashPNG = new Hud::CSplashImage( 0.2f, m_SplashFilename.c_str(), 370 fp4( g_Settings()->Get( "settings.app.pngfadein", 10 ) ), 371 fp4( g_Settings()->Get( "settings.app.pnghold", 10 ) ), 372 fp4( g_Settings()->Get( "settings.app.pngfadeout", 10 ) ) 373 ); 374 375 376 m_spCrossFade = new Hud::CCrossFade( g_Player().Display()->Width(), g_Player().Display()->Height(), true ); 377 378 // Start downloader. 379 g_Log->Info( "Starting downloader..." ); 380 381 g_ContentDownloader().Startup( false, m_MultipleInstancesMode ); 382 383 //call static method to fill sheep counts 384 ContentDownloader::Shepherd::GetFlockSizeMBsRecount(0); 385 ContentDownloader::Shepherd::GetFlockSizeMBsRecount(1); 386 // For testing... 387 //ContentDownloader::Shepherd::addMessageText( "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua", 50, 18 ); 388 389 // And we're off. 390 m_SplashPNGDelayTimer.Reset(); 391 m_Timer.Reset(); 392 g_Player().Start(); 393 m_F1F4Timer.Reset(); 394 m_LastCPUCheckTime = m_Timer.Time(); 395 return true; 396 } 397 398 // Shutdown()399 virtual void Shutdown() 400 { 401 printf( "CElectricSheep::Shutdown()\n" ); 402 403 #ifdef DO_THREAD_UPDATE 404 DestroyUpdateThreads(); 405 #endif 406 407 m_spSplashPos = NULL; 408 m_spSplashNeg = NULL; 409 m_spSplashPNG = NULL; 410 m_nSplashes = 0; 411 m_SplashFilename = std::string(); 412 m_spCrossFade = NULL; 413 m_StartupScreen = NULL; 414 m_HudManager = NULL; 415 416 if( !m_bConfigMode ) 417 { 418 g_ContentDownloader().Shutdown(); 419 420 // This stuff was never started in config mode. 421 if (m_MultipleInstancesMode == false) 422 { 423 SAFE_DELETE( m_pVoter ); 424 425 g_NetworkManager->Shutdown(); 426 } 427 g_Player().Shutdown(); 428 g_Settings()->Shutdown(); 429 } 430 } 431 Run()432 bool Run() 433 { 434 while( true ) 435 { 436 //g_Player().Renderer()->BeginFrame(); 437 438 if( !Update() ) 439 { 440 g_Player().Renderer()->EndFrame(); 441 return false; 442 } 443 } 444 445 return true; 446 } 447 FormatTimeDiff(uint64 timediff,bool showseconds)448 std::string FormatTimeDiff(uint64 timediff, bool showseconds) 449 { 450 std::stringstream ss; 451 452 // Prettify uptime. 453 uint32 seconds = ( timediff % 60 ); 454 uint32 minutes = (timediff / 60) % 60; 455 uint32 hours = (timediff / 3600) % 24; 456 uint32 days = uint32((timediff / (60*60*24))); 457 const char *space = ""; 458 if (days > 0) 459 { 460 ss << days << " day" << ((days != 1) ? "s" : "") << ","; 461 space = " "; 462 } 463 464 if (!showseconds) 465 { 466 if (days > 0) 467 { 468 if (hours > 0) 469 ss << space << hours << " hour" << ((hours != 1) ? "s" : ""); 470 else if (minutes > 0) 471 ss << space << minutes << " minute" << ((minutes != 1) ? "s" : ""); 472 else if (seconds > 0) 473 ss << space << seconds << " second" << ((seconds != 1) ? "s" : ""); 474 } 475 else 476 { 477 ss << space; 478 479 if (hours > 0) 480 ss << space << std::setfill('0') << std::setw(2) << hours << "h"; 481 482 ss << std::setfill('0') << std::setw(2) << minutes << "m" << std::setfill('0') << std::setw(2) << seconds << "s"; 483 } 484 } 485 else 486 { 487 ss << space; 488 489 if (hours > 0) 490 ss << space << std::setfill('0') << std::setw(2) << hours << "h"; 491 492 ss << std::setfill('0') << std::setw(2) << minutes << "m" << std::setfill('0') << std::setw(2) << seconds << "s"; 493 } 494 495 return ss.str(); 496 } 497 498 #ifdef DO_THREAD_UPDATE 499 // CreateUpdateThreads()500 virtual void CreateUpdateThreads() 501 { 502 int displayCnt = g_Player().GetDisplayCount(); 503 504 m_pUpdateBarrier = new boost::barrier( displayCnt + 1 ); 505 506 m_pUpdateThreads = new boost::thread_group; 507 508 for (int i = 0; i < displayCnt; i++) 509 { 510 boost::thread* th = new boost::thread(&CElectricSheep::UpdateFrameThread, this, i); 511 #ifdef WIN32 512 SetThreadPriority( (HANDLE)th->native_handle(), THREAD_PRIORITY_HIGHEST ); 513 SetThreadPriorityBoost( (HANDLE)th->native_handle(), FALSE ); 514 #else 515 struct sched_param sp; 516 sp.sched_priority = sched_get_priority_max(SCHED_RR); //HIGH_PRIORITY_CLASS - THREAD_PRIORITY_NORMAL 517 pthread_setschedparam( (pthread_t)th->native_handle(), SCHED_RR, &sp ); 518 #endif 519 m_pUpdateThreads->add_thread(th); 520 } 521 } 522 523 // DestroyUpdateThreads()524 virtual void DestroyUpdateThreads() 525 { 526 if ( m_pUpdateThreads != NULL ) 527 { 528 m_pUpdateThreads->interrupt_all(); 529 m_pUpdateThreads->join_all(); 530 531 SAFE_DELETE(m_pUpdateThreads); 532 } 533 534 SAFE_DELETE(m_pUpdateBarrier); 535 } 536 #endif 537 538 // Update()539 virtual bool Update() 540 { 541 g_Player().BeginFrameUpdate(); 542 543 #ifdef DO_THREAD_UPDATE 544 { 545 boost::mutex::scoped_lock lock( m_BarrierMutex ); 546 547 m_pUpdateBarrier->wait(); 548 549 m_pUpdateBarrier->wait(); 550 } 551 #else 552 uint32 displayCnt = g_Player().GetDisplayCount(); 553 554 bool ret = true; 555 for (uint32 i = 0; i < displayCnt; i++) 556 { 557 ret &= DoRealFrameUpdate(i); 558 if ( !ret ) 559 break; 560 } 561 #endif 562 563 g_Player().EndFrameUpdate(); 564 565 return ret; 566 } 567 568 #ifdef DO_THREAD_UPDATE UpdateFrameThread(int32 displayUnit)569 virtual void UpdateFrameThread(int32 displayUnit) 570 { 571 try { 572 while (true) 573 { 574 m_pUpdateBarrier->wait(); 575 576 DoRealFrameUpdate(displayUnit); 577 578 m_pUpdateBarrier->wait(); 579 } 580 } 581 catch(boost::thread_interrupted const&) 582 { 583 } 584 } 585 #endif 586 DoRealFrameUpdate(uint32 displayUnit)587 virtual bool DoRealFrameUpdate(uint32 displayUnit) 588 { 589 if ( g_Player().BeginDisplayFrame( displayUnit ) ) 590 { 591 592 //g_Player().Renderer()->BeginFrame(); 593 594 if( g_Player().Closed() ) 595 { 596 g_Log->Info( "Player closed..." ); 597 g_Player().Renderer()->EndFrame(); 598 return false; 599 } 600 601 bool drawNoSheepIntro = false; 602 bool drawn = g_Player().Update(displayUnit, drawNoSheepIntro); 603 604 if ( (drawNoSheepIntro && displayUnit == 0) || (drawn && displayUnit == 0) ) 605 { 606 if (drawNoSheepIntro) 607 { 608 if (m_StartupScreen.IsNull()) 609 m_StartupScreen = new Hud::CStartupScreen(Base::Math::CRect(0,0,1.,1.), "Trebuchet MS", 24); 610 m_StartupScreen->Render(0., g_Player().Renderer()); 611 } 612 // Process any server messages. 613 std::string msg; 614 fp8 duration; 615 616 if ( !m_spSplashPNG.IsNull() ) 617 { 618 if ( m_SplashPNGDelayTimer.Time() > m_PNGDelayTimer) 619 { 620 621 622 // update m_spSplashPNG here, so every time it is shown, it is randomized among our shuffle group. 623 624 if (m_nSplashes > 0) 625 { 626 char fNameFormatted[FILENAME_MAX]; 627 snprintf( fNameFormatted, FILENAME_MAX, m_SplashFilename.c_str(), rand() % m_nSplashes ); 628 if ( m_SplashFilename.empty() == false && g_Settings()->Get( "settings.app.attributionpng", true ) == true ) 629 m_spSplashPNG = new Hud::CSplashImage( 0.2f, fNameFormatted, 630 fp4( g_Settings()->Get( "settings.app.pngfadein", 10 ) ), 631 fp4( g_Settings()->Get( "settings.app.pnghold", 10 ) ), 632 fp4( g_Settings()->Get( "settings.app.pngfadeout", 10 ) ) 633 ); 634 } 635 636 637 638 m_HudManager->Add( "splash_png", m_spSplashPNG, 639 fp4( g_Settings()->Get( "settings.app.pngfadein", 10 ) + 640 g_Settings()->Get( "settings.app.pnghold", 10 ) + 641 g_Settings()->Get( "settings.app.pngfadeout", 10 ) ) 642 ); 643 m_SplashPNGDelayTimer.Reset(); 644 } 645 } 646 647 if( ContentDownloader::Shepherd::PopMessage( msg, duration )) 648 { 649 bool addtohud = true; 650 time_t lt = time(NULL); 651 652 if (( msg == "error connecting to server" ) || 653 ( msg == "server request failed, using free one" )) 654 { 655 std::string temptime = ctime(<); 656 msg = temptime.substr(0, temptime.length() - strlen("\n")) + " " + msg; 657 g_Log->Error(msg.c_str()); 658 if (m_ConnectionErrors.size() > 20) 659 m_ConnectionErrors.pop_front(); 660 m_ConnectionErrors.push_back(msg); 661 if (g_Settings()->Get( "settings.player.quiet_mode", true ) == true) 662 addtohud = false; 663 } 664 665 if (addtohud == true) 666 { 667 m_HudManager->Add( "servermessage", new Hud::CServerMessage( msg, Base::Math::CRect( 1, 1 ), 24 ), duration ); 668 m_HudManager->HideAll(); 669 } 670 } 671 if (m_F1F4Timer.Time() > 20*60) 672 { 673 m_F1F4Timer.Reset(); 674 m_HudManager->Hide( "helpmessage" ); 675 m_HudManager->Hide( "serverstats" ); 676 m_HudManager->Hide( "renderstats" ); 677 m_HudManager->Hide( "displaystats" ); 678 } 679 680 681 std::string batteryStatus = "Unknown"; 682 bool blockRendering = false; 683 if (m_Timer.Time() > m_LastCPUCheckTime + 3.) 684 { 685 m_LastCPUCheckTime = m_Timer.Time(); 686 if (m_CpuUsage.GetCpuUsage(m_CpuUsageTotal, m_CpuUsageES)) 687 if (m_CpuUsageTotal != -1 && m_CpuUsageES != -1 && m_CpuUsageTotal > m_CpuUsageES) 688 { 689 if ((m_CpuUsageTotal - m_CpuUsageES) > m_CpuUsageThreshold) 690 { 691 ++m_HighCpuUsageCounter; 692 if (m_HighCpuUsageCounter > 10) 693 { 694 m_HighCpuUsageCounter = 5; 695 blockRendering = true; 696 } 697 } else 698 { 699 if (m_HighCpuUsageCounter > 0) 700 --m_HighCpuUsageCounter; 701 } 702 } 703 } 704 if (m_HighCpuUsageCounter > 0 && ContentDownloader::Shepherd::RenderingAllowed() == false) 705 blockRendering = true; 706 707 switch( GetACLineStatus() ) 708 { 709 case 1: 710 { 711 batteryStatus = "Power adapter"; 712 /*m_PlayerFps = m_OriginalFps; 713 m_CurrentFps = m_PlayerFps; 714 g_Player().Framerate( m_CurrentFps );*/ 715 break; 716 } 717 case 0: 718 { 719 batteryStatus = "Battery"; 720 /*m_PlayerFps = m_OriginalFps/2; // half speed on battery power 721 m_CurrentFps = m_PlayerFps; 722 g_Player().Framerate( m_CurrentFps );*/ 723 blockRendering = true; 724 break; 725 } 726 case 255: 727 { 728 batteryStatus = "Unknown1"; 729 break; 730 } 731 default: 732 { 733 batteryStatus = "Unknown2"; 734 break; 735 } 736 } 737 738 if (blockRendering) 739 ContentDownloader::Shepherd::SetRenderingAllowed(false); 740 else 741 ContentDownloader::Shepherd::SetRenderingAllowed(true); 742 743 // Update some stats. 744 Hud::spCStatsConsole spStats = (Hud::spCStatsConsole)m_HudManager->Get( "displaystats" ); 745 std::stringstream decodefpsstr; 746 decodefpsstr.precision(2); 747 decodefpsstr << std::fixed << m_CurrentFps << " fps"; 748 ((Hud::CStringStat *)spStats->Get( "decodefps" ))->SetSample( decodefpsstr.str() ); 749 ((Hud::CIntCounter *)spStats->Get( "displayfps" ))->AddSample( 1 ); 750 751 uint32 playingID = g_Player().GetCurrentPlayingSheepID(); 752 uint32 playingGen = g_Player().GetCurrentPlayingSheepGeneration(); 753 uint16 playCnt = g_PlayCounter().PlayCount( playingGen, playingID ) - 1; 754 755 char strCurID[256]; 756 if (m_curPlayingID != playingID || m_curPlayingGen != playingGen) 757 { 758 if (playCnt > 0) 759 { 760 time_t lastatime = g_Player().GetCurrentPlayingatime(); 761 time_t currenttime = time(NULL); 762 m_lastPlayedSeconds = (uint64)floor(difftime(currenttime, lastatime)); 763 } 764 else 765 m_lastPlayedSeconds = 0; 766 767 m_curPlayingID = playingID; 768 m_curPlayingGen = playingGen; 769 } 770 771 char playCntStr[128]; 772 773 if (playCnt > 0) 774 sprintf(playCntStr, "Played: %hu time%s %s\nLast time: %s ago", 775 playCnt, 776 (playCnt == 1) ? "" : "s", 777 ( g_PlayCounter().ReadOnlyPlayCounts() ) ? " (not updated, read-only instance)" : "", 778 FormatTimeDiff(m_lastPlayedSeconds, false).c_str() 779 ); 780 else 781 strcpy(playCntStr, "Playing for the first time"); 782 783 snprintf( strCurID, 256, "#%d.%05d (%s)\n%s\n", 784 g_Player().GetCurrentPlayingGeneration(), 785 playingID, 786 g_Player().IsCurrentPlayingEdge() ? "edge" : "loop", 787 playCntStr 788 ); 789 if (playingID != 0) 790 ((Hud::CStringStat *)spStats->Get( "currentid" ))->SetSample( strCurID ); 791 792 // Prettify uptime. 793 uint64 uptime = (uint64)m_Timer.Time(); 794 795 char strHP[128]; 796 snprintf( strHP, 127, "%s", FormatTimeDiff(uptime, true).c_str() ); 797 ((Hud::CStringStat *)spStats->Get( "uptime" ))->SetSample( strHP ); 798 799 // Serverstats. 800 spStats = (Hud::spCStatsConsole)m_HudManager->Get( "serverstats" ); 801 802 std::stringstream tmpstr; 803 uint64 flockcount = 0; 804 uint64 flockmbs = 0; 805 uint64 flockcountfree = ContentDownloader::Shepherd::getClientFlockCount(0); 806 uint64 flockcountgold = ContentDownloader::Shepherd::getClientFlockCount(1); 807 uint64 flockmbsfree = ContentDownloader::Shepherd::getClientFlockMBs(0); 808 uint64 flockmbsgold = ContentDownloader::Shepherd::getClientFlockMBs(1); 809 switch ( g_Player().UsedSheepType() ) 810 { 811 case 0: // only gold, if any 812 { 813 flockcount = flockcountgold; 814 flockmbs = flockmbsgold; 815 if (g_Player().HasGoldSheep() == false) 816 { 817 flockcount += flockcountfree; 818 flockmbs += flockmbsfree; 819 } 820 } 821 break; 822 case 1: // free sheep only 823 flockcount = flockcountfree; 824 flockmbs = flockmbsfree; 825 break; 826 case 2: // all sheep 827 flockcount = flockcountfree + flockcountgold; 828 flockmbs = flockmbsfree + flockmbsgold; 829 break; 830 }; 831 tmpstr << flockcount << ((g_Player().UsedSheepType() == 0 && g_Player().HasGoldSheep() == true) ? " gold sheep, " : " sheep, ") << flockmbs << "MB"; 832 ((Hud::CStringStat *)spStats->Get( "all" ))->SetSample(tmpstr.str()); 833 834 const char *servername = ContentDownloader::Shepherd::serverName( false ); 835 if ( servername != NULL && servername[0] ) 836 { 837 ((Hud::CStringStat *)spStats->Get( "server" ))->SetSample( servername ); 838 ((Hud::CStringStat *)spStats->Get( "server" ))->Visible( true ); 839 } 840 else 841 ((Hud::CStringStat *)spStats->Get( "server" ))->Visible( false ); 842 843 844 Hud::CStringStat *pTmp = (Hud::CStringStat *)spStats->Get( "transfers" ); 845 if( pTmp ) 846 { 847 std::string serverStatus = g_NetworkManager->Status(); 848 if( serverStatus == "" ) 849 pTmp->Visible( false ); 850 else 851 { 852 pTmp->SetSample( serverStatus ); 853 pTmp->Visible( true ); 854 } 855 } 856 857 pTmp = (Hud::CStringStat *)spStats->Get( "loginstatus" ); 858 if( pTmp ) 859 { 860 bool visible = true; 861 862 const char *role = ContentDownloader::Shepherd::role(); 863 std::string loginstatus; 864 if (role != NULL) 865 loginstatus = role; 866 else 867 visible = false; 868 869 if( loginstatus.empty() || loginstatus == "none" ) 870 { 871 pTmp->SetSample( "Not logged in" ); 872 } 873 else 874 { 875 std::stringstream loginstatusstr; 876 loginstatusstr << "Logged in as " << ContentDownloader::SheepGenerator::nickName() << " (" << loginstatus << ")"; 877 pTmp->SetSample( loginstatusstr.str() ); 878 } 879 880 pTmp->Visible( visible ); 881 } 882 883 pTmp = (Hud::CStringStat *)spStats->Get( "bsurvivors" ); 884 if( pTmp ) 885 { 886 std::stringstream survivors; 887 888 survivors << "Survivors: median cut=" << g_PlayCounter().GetMedianCutSurvivors(); 889 890 if (m_SeamlessPlayback) 891 survivors << ", dead end eliminator=" << g_PlayCounter().GetDeadEndCutSurvivors(); 892 893 pTmp->SetSample( survivors.str() ); 894 pTmp->Visible( true ); 895 } 896 897 pTmp = (Hud::CStringStat *)spStats->Get( "deleted" ); 898 if( pTmp ) 899 { 900 std::string deleted; 901 if (ContentDownloader::Shepherd::PopOverflowMessage( deleted ) && (deleted != "")) 902 { 903 pTmp->SetSample( deleted ); 904 pTmp->Visible( true ); 905 } else 906 pTmp->Visible( false ); 907 } 908 909 pTmp = (Hud::CStringStat *)spStats->Get( "zconnerror" ); 910 if( pTmp ) 911 { 912 if (m_ConnectionErrors.size() > 0) 913 { 914 std::string allConnectionErrors; 915 for (size_t ii = 0; ii < m_ConnectionErrors.size(); ++ii) 916 allConnectionErrors += m_ConnectionErrors.at(ii) + "\n"; 917 if (allConnectionErrors.size() > 0) 918 allConnectionErrors.erase(allConnectionErrors.size() - 1); 919 pTmp->SetSample( allConnectionErrors ); 920 pTmp->Visible( true ); 921 } else 922 pTmp->Visible( false ); 923 } 924 925 Hud::CTimeCountDownStat *pTcd = (Hud::CTimeCountDownStat *)spStats->Get( "svstat" ); 926 if( pTcd ) 927 { 928 bool isnew = false; 929 930 std::string dlState = ContentDownloader::Shepherd::downloadState( isnew ); 931 932 if ( isnew ) 933 { 934 pTcd->SetSample( dlState ); 935 pTcd->Visible( true ); 936 } 937 } 938 939 // Renderer stats. 940 spStats = (Hud::spCStatsConsole)m_HudManager->Get( "renderstats" ); 941 ((Hud::CIntCounter *)spStats->Get( "rendering" ))->SetSample( ContentDownloader::Shepherd::FramesRendering() ); 942 ((Hud::CIntCounter *)spStats->Get( "totalframes" ))->SetSample( ContentDownloader::Shepherd::TotalFramesRendered() ); 943 944 Hud::CStringStat *batteryStat = ((Hud::CStringStat *)spStats->Get( "zbattery" )); 945 946 if (batteryStat != NULL) 947 batteryStat->SetSample( batteryStatus ); 948 949 if (m_CpuUsageTotal != -1 && m_CpuUsageES != -1) 950 { 951 std::stringstream temp; 952 953 temp << " ES " << m_CpuUsageES << "%, total " << m_CpuUsageTotal << "% "; 954 955 if ( ContentDownloader::Shepherd::RenderingAllowed() ) 956 temp << "(new rendering allowed)"; 957 else 958 temp << "(new rendering blocked)"; 959 960 ((Hud::CStringStat *)spStats->Get( "zzacpu" ))->SetSample( temp.str() ); 961 } 962 963 pTcd = (Hud::CTimeCountDownStat *)spStats->Get( "countdown" ); 964 if( pTcd ) 965 { 966 bool isnew = false; 967 968 std::string renderState = ContentDownloader::Shepherd::renderState( isnew ); 969 970 if ( isnew ) 971 { 972 pTcd->SetSample( renderState ); 973 pTcd->Visible( true ); 974 } 975 } 976 977 // Finally render hud. 978 m_HudManager->Render( g_Player().Renderer() ); 979 980 // Update display events. 981 g_Player().Display()->Update(); 982 } 983 984 g_Player().EndDisplayFrame( displayUnit, drawn ); 985 } 986 987 return true; 988 989 } 990 HandleOneEvent(DisplayOutput::spCEvent & _event)991 virtual bool HandleOneEvent( DisplayOutput::spCEvent &_event ) 992 { 993 static const fp4 voteDelaySeconds = 1; 994 995 if( _event->Type() == DisplayOutput::CEvent::Event_KEY ) 996 { 997 DisplayOutput::spCKeyEvent spKey = static_cast<DisplayOutput::spCKeyEvent>( _event ); 998 switch( spKey->m_Code ) 999 { 1000 // Vote for sheep. 1001 case DisplayOutput::CKeyEvent::KEY_UP: 1002 if( m_pVoter != NULL && m_pVoter->Vote( g_Player().GetCurrentPlayingID(), true, voteDelaySeconds ) ) 1003 m_HudManager->Add( "splash_pos", m_spSplashPos, voteDelaySeconds*0.9f ); 1004 break; 1005 case DisplayOutput::CKeyEvent::KEY_DOWN: 1006 if( m_pVoter != NULL && m_pVoter->Vote( g_Player().GetCurrentPlayingID(), false, voteDelaySeconds ) ) 1007 { 1008 if (g_Settings()->Get( "settings.content.negvotedeletes", true )) 1009 { 1010 //g_Player().Stop(); 1011 m_spCrossFade->Reset(); 1012 m_HudManager->Add( "fade", m_spCrossFade, 1.5 ); 1013 } 1014 1015 m_HudManager->Add( "splash_pos", m_spSplashNeg, voteDelaySeconds*0.9f ); 1016 } 1017 break; 1018 1019 // Repeat current sheep 1020 case DisplayOutput::CKeyEvent::KEY_LEFT: 1021 g_Player().ReturnToPrevious(); 1022 break; 1023 1024 // Force Next Sheep 1025 case DisplayOutput::CKeyEvent::KEY_RIGHT: 1026 g_Player().SkipToNext(); 1027 break; 1028 1029 // Repeat sheep 1030 case DisplayOutput::CKeyEvent::KEY_F8: 1031 g_Player().RepeatSheep(); 1032 break; 1033 1034 // OSD info. 1035 1036 // OSD info. 1037 case DisplayOutput::CKeyEvent::KEY_F1: 1038 m_F1F4Timer.Reset(); 1039 m_HudManager->Toggle( "helpmessage" ); 1040 break; 1041 case DisplayOutput::CKeyEvent::KEY_F2: 1042 m_F1F4Timer.Reset(); 1043 m_HudManager->Toggle( "serverstats" ); 1044 break; 1045 case DisplayOutput::CKeyEvent::KEY_F3: 1046 m_F1F4Timer.Reset(); 1047 m_HudManager->Toggle( "renderstats" ); 1048 break; 1049 case DisplayOutput::CKeyEvent::KEY_F4: 1050 m_F1F4Timer.Reset(); 1051 m_HudManager->Toggle( "displaystats" ); 1052 break; 1053 1054 // All other keys needs to be ignored, they are handled somewhere else... 1055 default: 1056 { 1057 g_Log->Info( "Key event, ignoring" ); 1058 return false; 1059 } 1060 } 1061 return true; 1062 } 1063 return false; 1064 } 1065 HandleEvents()1066 virtual bool HandleEvents() 1067 { 1068 DisplayOutput::spCDisplayOutput spDisplay = g_Player().Display(); 1069 1070 // Handle events. 1071 DisplayOutput::spCEvent spEvent; 1072 while( spDisplay->GetEvent( spEvent ) ) 1073 { 1074 if ( HandleOneEvent( spEvent ) == false ) 1075 return false; 1076 } 1077 1078 return true; 1079 } 1080 GetVersion()1081 virtual std::string GetVersion() 1082 { 1083 return ""; 1084 } 1085 GetACLineStatus()1086 virtual int GetACLineStatus() 1087 { 1088 return -1; 1089 } 1090 SetUpdateAvailable(const std::string & verinfo)1091 void SetUpdateAvailable(const std::string& verinfo) 1092 { 1093 std::string message("New Electric Sheep "); 1094 1095 message += verinfo; 1096 message += " is available."; 1097 #ifdef MAC 1098 message += " Relaunch ES application or preference pane to update."; 1099 #endif 1100 1101 ContentDownloader::Shepherd::QueueMessage( message, 30.0 ); 1102 } 1103 }; 1104 1105 #endif // CLIENT_H_INCLUDED 1106