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(&lt);
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