1 /*****************************************************************************
2  * PokerTH - The open source texas holdem engine                             *
3  * Copyright (C) 2006-2012 Felix Hammer, Florian Thauer, Lothar May          *
4  *                                                                           *
5  * This program is free software: you can redistribute it and/or modify      *
6  * it under the terms of the GNU Affero General Public License as            *
7  * published by the Free Software Foundation, either version 3 of the        *
8  * License, or (at your option) any later version.                           *
9  *                                                                           *
10  * This program is distributed in the hope that it will be useful,           *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
13  * GNU Affero General Public License for more details.                       *
14  *                                                                           *
15  * You should have received a copy of the GNU Affero General Public License  *
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.     *
17  *                                                                           *
18  *                                                                           *
19  * Additional permission under GNU AGPL version 3 section 7                  *
20  *                                                                           *
21  * If you modify this program, or any covered work, by linking or            *
22  * combining it with the OpenSSL project's OpenSSL library (or a             *
23  * modified version of that library), containing parts covered by the        *
24  * terms of the OpenSSL or SSLeay licenses, the authors of PokerTH           *
25  * (Felix Hammer, Florian Thauer, Lothar May) grant you additional           *
26  * permission to convey the resulting work.                                  *
27  * Corresponding Source for a non-source form of such a combination          *
28  * shall include the source code for the parts of OpenSSL used as well       *
29  * as that of the covered work.                                              *
30  *****************************************************************************/
31 
32 #include <net/serverlobbythread.h>
33 #include <net/servergame.h>
34 #include <net/serverbanmanager.h>
35 #include <net/serverexception.h>
36 #include <net/receivebuffer.h>
37 #include <net/senderhelper.h>
38 #include <net/serverircbotcallback.h>
39 #include <net/socket_msg.h>
40 #include <net/chatcleanermanager.h>
41 #include <net/net_helper.h>
42 #include <db/serverdbinterface.h>
43 #ifdef POKERTH_OFFICIAL_SERVER
44 #include <dbofficial/serverdbfactoryinternal.h>
45 #else
46 #include <db/serverdbfactorygeneric.h>
47 #endif
48 #include <core/avatarmanager.h>
49 #include <core/loghelper.h>
50 #include <core/openssl_wrapper.h>
51 #include <configfile.h>
52 #include <playerinterface.h>
53 
54 #include <fstream>
55 #include <sstream>
56 #include <algorithm>
57 #include <cctype>
58 #include <boost/lambda/lambda.hpp>
59 #include <boost/filesystem.hpp>
60 #include <boost/bind.hpp>
61 #include <boost/foreach.hpp>
62 #include <boost/uuid/uuid.hpp>
63 #include <boost/algorithm/string.hpp>
64 #include <gsasl.h>
65 
66 #define SERVER_MAX_NUM_LOBBY_SESSIONS				512		// Maximum number of idle users in lobby.
67 #define SERVER_MAX_NUM_TOTAL_SESSIONS				2000	// Total maximum of sessions, fitting a 2048 handle limit
68 
69 #define SERVER_SAVE_STATISTICS_INTERVAL_SEC			60
70 #define SERVER_CHECK_SESSION_TIMEOUTS_INTERVAL_MSEC	500
71 #define SERVER_REMOVE_GAME_INTERVAL_MSEC			500
72 #define SERVER_REMOVE_PLAYER_INTERVAL_MSEC			100
73 #define SERVER_UPDATE_LOGIN_LOCK_INTERVAL_MSEC		1000
74 #define SERVER_PROCESS_SEND_INTERVAL_MSEC			10
75 
76 #define SERVER_INIT_LOGIN_CLIENT_LOCK_SEC			NetHelper::GetLoginLockSec()
77 
78 #define SERVER_INIT_SESSION_TIMEOUT_SEC				60
79 #define SERVER_TIMEOUT_WARNING_REMAINING_SEC		60
80 #define SERVER_SESSION_ACTIVITY_TIMEOUT_SEC			1800	// 30 min, MUST be > SERVER_TIMEOUT_WARNING_REMAINING_SEC
81 #define SERVER_SESSION_FORCED_TIMEOUT_SEC			86400	// 1 day, should be quite large.
82 
83 #define SERVER_ADDRESS_LOCALHOST_STR_V4				"127.0.0.1"
84 #define SERVER_ADDRESS_LOCALHOST_STR_V4V6			"::ffff:127.0.0.1"
85 #define SERVER_ADDRESS_LOCALHOST_STR				"::1"
86 
87 #define SERVER_STATISTICS_FILE_NAME					"server_statistics.log"
88 #define SERVER_STATISTICS_STR_TOTAL_PLAYERS			"TotalNumPlayersLoggedIn"
89 #define SERVER_STATISTICS_STR_TOTAL_GAMES			"TotalNumGamesCreated"
90 #define SERVER_STATISTICS_STR_MAX_GAMES				"MaxGamesOpen"
91 #define SERVER_STATISTICS_STR_MAX_PLAYERS			"MaxPlayersLoggedIn"
92 #define SERVER_STATISTICS_STR_CUR_GAMES				"CurGamesOpen"
93 #define SERVER_STATISTICS_STR_CUR_PLAYERS			"CurPlayersLoggedIn"
94 
95 using namespace std;
96 using boost::asio::ip::tcp;
97 
98 #ifdef BOOST_ASIO_HAS_STD_CHRONO
99 using namespace std::chrono;
100 #else
101 using namespace boost::chrono;
102 #endif
103 
104 class InternalServerCallback : public SessionDataCallback, public ChatCleanerCallback, public ServerDBCallback
105 {
106 public:
InternalServerCallback(ServerLobbyThread & server)107 	InternalServerCallback(ServerLobbyThread &server) : m_server(server) {}
~InternalServerCallback()108 	virtual ~InternalServerCallback() {}
109 
CloseSession(boost::shared_ptr<SessionData> session)110 	virtual void CloseSession(boost::shared_ptr<SessionData> session)
111 	{
112 		m_server.CloseSession(session);
113 	}
114 
SessionError(boost::shared_ptr<SessionData> session,int errorCode)115 	virtual void SessionError(boost::shared_ptr<SessionData> session, int errorCode)
116 	{
117 		m_server.SessionError(session, errorCode);
118 	}
119 
SessionTimeoutWarning(boost::shared_ptr<SessionData> session,unsigned remainingSec)120 	virtual void SessionTimeoutWarning(boost::shared_ptr<SessionData> session, unsigned remainingSec)
121 	{
122 		m_server.SessionTimeoutWarning(session, remainingSec);
123 	}
124 
HandlePacket(boost::shared_ptr<SessionData> session,boost::shared_ptr<NetPacket> packet)125 	virtual void HandlePacket(boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
126 	{
127 		m_server.DispatchPacket(session, packet);
128 	}
129 
SignalChatBotMessage(const string & msg)130 	virtual void SignalChatBotMessage(const string &msg)
131 	{
132 		m_server.SendChatBotMsg(msg);
133 	}
134 
SignalChatBotMessage(unsigned gameId,const std::string & msg)135 	virtual void SignalChatBotMessage(unsigned gameId, const std::string &msg)
136 	{
137 		m_server.SendChatBotMsg(gameId, msg);
138 	}
139 
SignalKickPlayer(unsigned playerId)140 	virtual void SignalKickPlayer(unsigned playerId)
141 	{
142 		m_server.RemovePlayer(playerId, ERR_NET_PLAYER_KICKED);
143 	}
144 
SignalBanPlayer(unsigned playerId)145 	virtual void SignalBanPlayer(unsigned playerId)
146 	{
147 		string playerName(m_server.GetPlayerNameFromId(playerId));
148 		if (!playerName.empty()) {
149 			m_server.GetBanManager().BanPlayerName(playerName, 1);
150 			m_server.RemovePlayer(playerId, ERR_NET_PLAYER_KICKED);
151 		}
152 	}
153 
SignalMutePlayer(unsigned playerId)154 	virtual void SignalMutePlayer(unsigned playerId)
155 	{
156 		m_server.MutePlayerInGame(playerId);
157 	}
158 
ConnectSuccess()159 	virtual void ConnectSuccess()
160 	{
161 		LOG_MSG("Successfully connected to database.");
162 	}
163 
ConnectFailed(string error)164 	virtual void ConnectFailed(string error)
165 	{
166 		LOG_ERROR("DB connect error: " << error);
167 	}
168 
QueryError(string error)169 	virtual void QueryError(string error)
170 	{
171 		LOG_ERROR("DB query error: " << error);
172 	}
173 
PlayerLoginSuccess(unsigned requestId,boost::shared_ptr<DBPlayerData> dbPlayerData)174 	virtual void PlayerLoginSuccess(unsigned requestId, boost::shared_ptr<DBPlayerData> dbPlayerData)
175 	{
176 		m_server.UserValid(requestId, *dbPlayerData);
177 	}
178 
PlayerLoginFailed(unsigned requestId)179 	virtual void PlayerLoginFailed(unsigned requestId)
180 	{
181 		m_server.UserInvalid(requestId);
182 	}
183 
PlayerLoginBlocked(unsigned requestId)184 	virtual void PlayerLoginBlocked(unsigned requestId)
185 	{
186 		m_server.UserBlocked(requestId);
187 	}
188 
AvatarIsBlacklisted(unsigned requestId)189 	virtual void AvatarIsBlacklisted(unsigned requestId)
190 	{
191 		m_server.AvatarBlacklisted(requestId);
192 	}
193 
AvatarIsOK(unsigned requestId)194 	virtual void AvatarIsOK(unsigned requestId)
195 	{
196 		m_server.AvatarOK(requestId);
197 	}
198 
CreateGameSuccess(unsigned)199 	virtual void CreateGameSuccess(unsigned /*requestId*/)
200 	{
201 		// Nothing to do.
202 	}
203 
CreateGameFailed(unsigned requestId)204 	virtual void CreateGameFailed(unsigned requestId)
205 	{
206 		// TODO maybe handle request id.
207 		LOG_ERROR("DB create game failed for request " << requestId);
208 	}
209 
ReportAvatarSuccess(unsigned requestId,unsigned replyId)210 	virtual void ReportAvatarSuccess(unsigned requestId, unsigned replyId)
211 	{
212 		m_server.SendReportAvatarResult(requestId, replyId, true);
213 	}
214 
ReportAvatarFailed(unsigned requestId,unsigned replyId)215 	virtual void ReportAvatarFailed(unsigned requestId, unsigned replyId)
216 	{
217 		m_server.SendReportAvatarResult(requestId, replyId, false);
218 	}
219 
ReportGameSuccess(unsigned requestId,unsigned replyId)220 	virtual void ReportGameSuccess(unsigned requestId, unsigned replyId)
221 	{
222 		m_server.SendReportGameResult(requestId, replyId, true);
223 	}
224 
ReportGameFailed(unsigned requestId,unsigned replyId)225 	virtual void ReportGameFailed(unsigned requestId, unsigned replyId)
226 	{
227 		m_server.SendReportGameResult(requestId, replyId, false);
228 	}
229 
PlayerAdminList(unsigned,std::list<DB_id> adminList)230 	virtual void PlayerAdminList(unsigned /*requestId*/, std::list<DB_id> adminList)
231 	{
232 		m_server.GetBanManager().SetAdminPlayerIds(adminList);
233 	}
234 
BlockPlayerSuccess(unsigned requestId,unsigned replyId)235 	virtual void BlockPlayerSuccess(unsigned requestId, unsigned replyId)
236 	{
237 		m_server.SendAdminBanPlayerResult(requestId, replyId, true);
238 	}
239 
BlockPlayerFailed(unsigned requestId,unsigned replyId)240 	virtual void BlockPlayerFailed(unsigned requestId, unsigned replyId)
241 	{
242 		m_server.SendAdminBanPlayerResult(requestId, replyId, false);
243 	}
244 
245 private:
246 	ServerLobbyThread &m_server;
247 };
248 
249 
ServerLobbyThread(GuiInterface & gui,ServerMode mode,ServerIrcBotCallback & ircBotCb,ConfigFile & serverConfig,AvatarManager & avatarManager,boost::shared_ptr<boost::asio::io_service> ioService)250 ServerLobbyThread::ServerLobbyThread(GuiInterface &gui, ServerMode mode, ServerIrcBotCallback &ircBotCb, ConfigFile &serverConfig,
251 									 AvatarManager &avatarManager, boost::shared_ptr<boost::asio::io_service> ioService)
252 	: m_ioService(ioService), m_authContext(NULL), m_gui(gui), m_ircBotCb(ircBotCb), m_avatarManager(avatarManager),
253 	  m_mode(mode), m_serverConfig(serverConfig), m_curGameId(0), m_curUniquePlayerId(0), m_curSessionId(INVALID_SESSION + 1),
254 	  m_statDataChanged(false), m_removeGameTimer(*ioService),
255 	  m_saveStatisticsTimer(*ioService), m_loginLockTimer(*ioService),
256 	  m_startTime(boost::posix_time::second_clock::local_time())
257 {
258 	m_internalServerCallback.reset(new InternalServerCallback(*this));
259 	m_sender.reset(new SenderHelper(m_ioService));
260 	m_banManager.reset(new ServerBanManager(m_ioService));
261 	m_chatCleanerManager.reset(new ChatCleanerManager(*m_internalServerCallback, m_ioService));
262 	DBFactory dbFactory;
263 	m_database = dbFactory.CreateServerDBObject(*m_internalServerCallback, m_ioService);
264 }
265 
~ServerLobbyThread()266 ServerLobbyThread::~ServerLobbyThread()
267 {
268 }
269 
270 void
Init(const string & logDir)271 ServerLobbyThread::Init(const string &logDir)
272 {
273 	// Read previous server statistics.
274 	if (!logDir.empty()) {
275 		boost::filesystem::path logPath(logDir);
276 		if (!logDir.empty()) {
277 			logPath /= SERVER_STATISTICS_FILE_NAME;
278 			m_statisticsFileName = logPath.directory_string();
279 			ReadStatisticsFile();
280 		}
281 	}
282 	m_database->Init(
283 		m_serverConfig.readConfigString("DBServerAddress"),
284 		m_serverConfig.readConfigString("DBServerUser"),
285 		m_serverConfig.readConfigString("DBServerPassword"),
286 		m_serverConfig.readConfigString("DBServerDatabaseName"),
287 		m_serverConfig.readConfigString("DBServerEncryptionKey"));
288 	m_database->AsyncQueryAdminPlayers(0);
289 
290 	GetBanManager().InitGameNameBadWordList(m_serverConfig.readConfigStringList("GameNameBadWordList"));
291 }
292 
293 void
SignalTermination()294 ServerLobbyThread::SignalTermination()
295 {
296 	Thread::SignalTermination();
297 	m_ioService->stop();
298 }
299 
300 void
AddConnection(boost::shared_ptr<SessionData> sessionData)301 ServerLobbyThread::AddConnection(boost::shared_ptr<SessionData> sessionData)
302 {
303 	// Create a new session.
304 	m_sessionManager.AddSession(sessionData);
305 
306 	LOG_VERBOSE("Accepted connection - session #" << sessionData->GetId() << ".");
307 
308 	sessionData->StartTimerInitTimeout(SERVER_INIT_SESSION_TIMEOUT_SEC);
309 	sessionData->StartTimerGlobalTimeout(SERVER_SESSION_FORCED_TIMEOUT_SEC);
310 	sessionData->StartTimerActivityTimeout(SERVER_SESSION_ACTIVITY_TIMEOUT_SEC, SERVER_TIMEOUT_WARNING_REMAINING_SEC);
311 
312 	unsigned numLobbySessions = m_sessionManager.GetRawSessionCount();
313 	unsigned numGameSessions = m_gameSessionManager.GetRawSessionCount();
314 	if (numLobbySessions <= SERVER_MAX_NUM_LOBBY_SESSIONS
315 			&& numLobbySessions + numGameSessions <= SERVER_MAX_NUM_TOTAL_SESSIONS) {
316 		string ipAddress = sessionData->GetRemoteIPAddressFromSocket();
317 		if (!ipAddress.empty()) {
318 			sessionData->SetClientAddr(ipAddress);
319 
320 			boost::shared_ptr<NetPacket> packet(new NetPacket);
321 			packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AnnounceMessage);
322 			AnnounceMessage *netAnnounce = packet->GetMsg()->mutable_announcemessage();
323 			netAnnounce->mutable_protocolversion()->set_majorversion(NET_VERSION_MAJOR);
324 			netAnnounce->mutable_protocolversion()->set_minorversion(NET_VERSION_MINOR);
325 			netAnnounce->mutable_latestgameversion()->set_majorversion(POKERTH_VERSION_MAJOR);
326 			netAnnounce->mutable_latestgameversion()->set_minorversion(POKERTH_VERSION_MINOR);
327 			netAnnounce->set_latestbetarevision(POKERTH_BETA_REVISION);
328 			switch (GetServerMode()) {
329 			case SERVER_MODE_LAN:
330 				netAnnounce->set_servertype(AnnounceMessage::serverTypeLAN);
331 				break;
332 			case SERVER_MODE_INTERNET_NOAUTH:
333 				netAnnounce->set_servertype(AnnounceMessage::serverTypeInternetNoAuth);
334 				break;
335 			case SERVER_MODE_INTERNET_AUTH:
336 				netAnnounce->set_servertype(AnnounceMessage::serverTypeInternetAuth);
337 				break;
338 			}
339 			{
340 				boost::mutex::scoped_lock lock(m_statMutex);
341 				netAnnounce->set_numplayersonserver(m_statData.numberOfPlayersOnServer);
342 			}
343 			GetSender().Send(sessionData, packet);
344 			sessionData->GetReceiveBuffer().StartAsyncRead(sessionData);
345 		} else {
346 			// We do not accept sessions if we cannot
347 			// retrieve the client address.
348 			SessionError(sessionData, ERR_NET_INVALID_SESSION);
349 		}
350 	} else {
351 		// Server is full.
352 		// Gracefully close this session.
353 		SessionError(sessionData, ERR_NET_SERVER_FULL);
354 	}
355 }
356 
357 void
ReAddSession(boost::shared_ptr<SessionData> session,int reason,unsigned gameId)358 ServerLobbyThread::ReAddSession(boost::shared_ptr<SessionData> session, int reason, unsigned gameId)
359 {
360 	if (session && session->GetPlayerData()) {
361 		boost::shared_ptr<NetPacket> packet(new NetPacket);
362 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_RemovedFromGameMessage);
363 		RemovedFromGameMessage *removed = packet->GetMsg()->mutable_removedfromgamemessage();
364 		removed->set_gameid(gameId);
365 
366 		switch (reason) {
367 		case NTF_NET_REMOVED_GAME_FULL :
368 			removed->set_removedfromgamereason(RemovedFromGameMessage::gameIsFull);
369 			break;
370 		case NTF_NET_REMOVED_ALREADY_RUNNING :
371 			removed->set_removedfromgamereason(RemovedFromGameMessage::gameIsRunning);
372 			break;
373 		case NTF_NET_REMOVED_KICKED :
374 			removed->set_removedfromgamereason(RemovedFromGameMessage::kickedFromGame);
375 			break;
376 		case NTF_NET_REMOVED_TIMEOUT :
377 			removed->set_removedfromgamereason(RemovedFromGameMessage::gameTimeout);
378 			break;
379 		case NTF_NET_REMOVED_START_FAILED :
380 			removed->set_removedfromgamereason(RemovedFromGameMessage::removedStartFailed);
381 			break;
382 		case NTF_NET_REMOVED_GAME_CLOSED :
383 			removed->set_removedfromgamereason(RemovedFromGameMessage::gameClosed);
384 			break;
385 		default :
386 			removed->set_removedfromgamereason(RemovedFromGameMessage::removedOnRequest);
387 			break;
388 		}
389 		GetSender().Send(session, packet);
390 
391 		HandleReAddedSession(session);
392 	}
393 }
394 
395 void
MoveSessionToGame(boost::shared_ptr<ServerGame> game,boost::shared_ptr<SessionData> session,bool autoLeave,bool spectateOnly)396 ServerLobbyThread::MoveSessionToGame(boost::shared_ptr<ServerGame> game, boost::shared_ptr<SessionData> session, bool autoLeave, bool spectateOnly)
397 {
398 	// Remove session from the lobby.
399 	m_sessionManager.RemoveSession(session->GetId());
400 	// Session is now in game state.
401 	session->SetState(spectateOnly ? SessionData::Spectating : SessionData::Game);
402 	// Store it in the list of game sessions.
403 	m_gameSessionManager.AddSession(session);
404 	// Set the game id of the session.
405 	session->SetGame(game);
406 	// Add session to the game.
407 	game->AddSession(session, spectateOnly);
408 	// Optionally enable auto leave after game finish.
409 	if (autoLeave)
410 		game->SetPlayerAutoLeaveOnFinish(session->GetPlayerData()->GetUniqueId());
411 }
412 
413 void
CloseSession(boost::shared_ptr<SessionData> session)414 ServerLobbyThread::CloseSession(boost::shared_ptr<SessionData> session)
415 {
416 	if (session && session->GetState() != SessionData::Closed) { // Make this call reentrant.
417 		LOG_VERBOSE("Closing session #" << session->GetId() << ".");
418 
419 		boost::shared_ptr<ServerGame> tmpGame = session->GetGame();
420 		if (tmpGame) {
421 			tmpGame->RemoveSession(session, NTF_NET_INTERNAL);
422 		}
423 		session->SetGame(boost::shared_ptr<ServerGame>());
424 		session->SetState(SessionData::Closed);
425 
426 		m_sessionManager.RemoveSession(session->GetId());
427 		m_gameSessionManager.RemoveSession(session->GetId());
428 
429 		if (session->GetPlayerData()) {
430 			NotifyPlayerLeftLobby(session->GetPlayerData()->GetUniqueId());
431 		}
432 		// Update stats (if needed).
433 		UpdateStatisticsNumberOfPlayers();
434 
435 		// Ignore error when shutting down the socket.
436 		boost::shared_ptr<boost::asio::ip::tcp::socket> sock = session->GetAsioSocket();
437 		if (sock) {
438 			boost::system::error_code ec;
439 			session->GetAsioSocket()->shutdown(boost::asio::ip::tcp::socket::shutdown_receive, ec);
440 		}
441 		// Close this session after send.
442 		GetSender().SetCloseAfterSend(session);
443 		// Cancel all timers of the session.
444 		session->CancelTimers();
445 	}
446 }
447 
448 void
ResubscribeLobbyMsg(boost::shared_ptr<SessionData> session)449 ServerLobbyThread::ResubscribeLobbyMsg(boost::shared_ptr<SessionData> session)
450 {
451 	InternalResubscribeMsg(session);
452 }
453 
454 void
NotifyPlayerJoinedLobby(unsigned playerId)455 ServerLobbyThread::NotifyPlayerJoinedLobby(unsigned playerId)
456 {
457 	boost::shared_ptr<NetPacket> notify = CreateNetPacketPlayerListNew(playerId);
458 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), notify, SessionData::Established);
459 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), notify, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
460 }
461 
462 void
NotifyPlayerLeftLobby(unsigned playerId)463 ServerLobbyThread::NotifyPlayerLeftLobby(unsigned playerId)
464 {
465 	boost::shared_ptr<NetPacket> notify = CreateNetPacketPlayerListLeft(playerId);
466 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), notify, SessionData::Established);
467 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), notify, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
468 }
469 
470 void
NotifyPlayerJoinedGame(unsigned gameId,unsigned playerId)471 ServerLobbyThread::NotifyPlayerJoinedGame(unsigned gameId, unsigned playerId)
472 {
473 	// Send notification to players in lobby.
474 	boost::shared_ptr<NetPacket> packet(new NetPacket);
475 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListPlayerJoinedMessage);
476 	GameListPlayerJoinedMessage *netListMsg = packet->GetMsg()->mutable_gamelistplayerjoinedmessage();
477 	netListMsg->set_gameid(gameId);
478 	netListMsg->set_playerid(playerId);
479 
480 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
481 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
482 }
483 
484 void
NotifyPlayerLeftGame(unsigned gameId,unsigned playerId)485 ServerLobbyThread::NotifyPlayerLeftGame(unsigned gameId, unsigned playerId)
486 {
487 	// Send notification to players in lobby.
488 	boost::shared_ptr<NetPacket> packet(new NetPacket);
489 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListPlayerLeftMessage);
490 	GameListPlayerLeftMessage *netListMsg = packet->GetMsg()->mutable_gamelistplayerleftmessage();
491 	netListMsg->set_gameid(gameId);
492 	netListMsg->set_playerid(playerId);
493 
494 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
495 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
496 }
497 
498 void
NotifySpectatorJoinedGame(unsigned gameId,unsigned playerId)499 ServerLobbyThread::NotifySpectatorJoinedGame(unsigned gameId, unsigned playerId)
500 {
501 	// Send notification to players in lobby.
502 	boost::shared_ptr<NetPacket> packet(new NetPacket);
503 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListSpectatorJoinedMessage);
504 	GameListSpectatorJoinedMessage *netListMsg = packet->GetMsg()->mutable_gamelistspectatorjoinedmessage();
505 	netListMsg->set_gameid(gameId);
506 	netListMsg->set_playerid(playerId);
507 
508 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
509 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
510 }
511 
512 void
NotifySpectatorLeftGame(unsigned gameId,unsigned playerId)513 ServerLobbyThread::NotifySpectatorLeftGame(unsigned gameId, unsigned playerId)
514 {
515 	// Send notification to players in lobby.
516 	boost::shared_ptr<NetPacket> packet(new NetPacket);
517 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListSpectatorLeftMessage);
518 	GameListSpectatorLeftMessage *netListMsg = packet->GetMsg()->mutable_gamelistspectatorleftmessage();
519 	netListMsg->set_gameid(gameId);
520 	netListMsg->set_playerid(playerId);
521 
522 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
523 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
524 }
525 
526 void
NotifyGameAdminChanged(unsigned gameId,unsigned newAdminPlayerId)527 ServerLobbyThread::NotifyGameAdminChanged(unsigned gameId, unsigned newAdminPlayerId)
528 {
529 	// Send notification to players in lobby.
530 	// Send notification to players in lobby.
531 	boost::shared_ptr<NetPacket> packet(new NetPacket);
532 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListAdminChangedMessage);
533 	GameListAdminChangedMessage *netListMsg = packet->GetMsg()->mutable_gamelistadminchangedmessage();
534 	netListMsg->set_gameid(gameId);
535 	netListMsg->set_newadminplayerid(newAdminPlayerId);
536 
537 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
538 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
539 }
540 
541 void
NotifyStartingGame(unsigned gameId)542 ServerLobbyThread::NotifyStartingGame(unsigned gameId)
543 {
544 	boost::shared_ptr<NetPacket> packet = CreateNetPacketGameListUpdate(gameId, GAME_MODE_STARTED);
545 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
546 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
547 }
548 
549 void
NotifyReopeningGame(unsigned gameId)550 ServerLobbyThread::NotifyReopeningGame(unsigned gameId)
551 {
552 	boost::shared_ptr<NetPacket> packet = CreateNetPacketGameListUpdate(gameId, GAME_MODE_CREATED);
553 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
554 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
555 }
556 
557 void
HandleGameRetrievePlayerInfo(boost::shared_ptr<SessionData> session,const PlayerInfoRequestMessage & playerInfoRequest)558 ServerLobbyThread::HandleGameRetrievePlayerInfo(boost::shared_ptr<SessionData> session, const PlayerInfoRequestMessage &playerInfoRequest)
559 {
560 	// Someone within a game requested player info.
561 	HandleNetPacketRetrievePlayerInfo(session, playerInfoRequest);
562 }
563 
564 void
HandleGameRetrieveAvatar(boost::shared_ptr<SessionData> session,const AvatarRequestMessage & retrieveAvatar)565 ServerLobbyThread::HandleGameRetrieveAvatar(boost::shared_ptr<SessionData> session, const AvatarRequestMessage &retrieveAvatar)
566 {
567 	// Someone within a game requested an avatar.
568 	HandleNetPacketRetrieveAvatar(session, retrieveAvatar);
569 }
570 
571 void
HandleGameReportGame(boost::shared_ptr<SessionData> session,const ReportGameMessage & reportGame)572 ServerLobbyThread::HandleGameReportGame(boost::shared_ptr<SessionData> session, const ReportGameMessage &reportGame)
573 {
574 	// Someone within a game reportet a game name.
575 	HandleNetPacketReportGame(session, reportGame);
576 }
577 
578 void
HandleChatRequest(boost::shared_ptr<SessionData> session,const ChatRequestMessage & chatRequest)579 ServerLobbyThread::HandleChatRequest(boost::shared_ptr<SessionData> session, const ChatRequestMessage &chatRequest)
580 {
581 	// Someone within a game sent a lobby message.
582 	HandleNetPacketChatRequest(session, chatRequest);
583 }
584 
585 void
HandleAdminRemoveGame(boost::shared_ptr<SessionData> session,const AdminRemoveGameMessage & removeGame)586 ServerLobbyThread::HandleAdminRemoveGame(boost::shared_ptr<SessionData> session, const AdminRemoveGameMessage &removeGame)
587 {
588 	// An admin within a game sent a remove game message.
589 	HandleNetPacketAdminRemoveGame(session, removeGame);
590 }
591 
592 void
HandleAdminBanPlayer(boost::shared_ptr<SessionData> session,const AdminBanPlayerMessage & banPlayer)593 ServerLobbyThread::HandleAdminBanPlayer(boost::shared_ptr<SessionData> session, const AdminBanPlayerMessage &banPlayer)
594 {
595 	// An admin within a game sent a ban player message.
596 	HandleNetPacketAdminBanPlayer(session, banPlayer);
597 }
598 
599 bool
KickPlayerByName(const std::string & playerName)600 ServerLobbyThread::KickPlayerByName(const std::string &playerName)
601 {
602 	bool retVal = false;
603 	boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByPlayerName(playerName);
604 	if (!session)
605 		session = m_gameSessionManager.GetSessionByPlayerName(playerName);
606 
607 	if (session && session->GetPlayerData()) {
608 		RemovePlayer(session->GetPlayerData()->GetUniqueId(), ERR_NET_PLAYER_KICKED);
609 		retVal = true;
610 	}
611 
612 	return retVal;
613 }
614 
615 bool
RemoveGameByPlayerName(const std::string & playerName)616 ServerLobbyThread::RemoveGameByPlayerName(const std::string &playerName)
617 {
618 	bool retVal = false;
619 	boost::shared_ptr<SessionData> session = m_gameSessionManager.GetSessionByPlayerName(playerName);
620 
621 	if (session) {
622 		boost::shared_ptr<ServerGame> game = session->GetGame();
623 		if (game) {
624 			m_ioService->post(boost::bind(&ServerLobbyThread::InternalRemoveGame, shared_from_this(), game));
625 			retVal = true;
626 		}
627 	}
628 
629 	return retVal;
630 }
631 
632 string
GetPlayerIPAddress(const std::string & playerName) const633 ServerLobbyThread::GetPlayerIPAddress(const std::string &playerName) const
634 {
635 	string ipAddress;
636 	boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByPlayerName(playerName);
637 	if (!session)
638 		session = m_gameSessionManager.GetSessionByPlayerName(playerName);
639 
640 	if (session)
641 		ipAddress = session->GetClientAddr();
642 
643 	return ipAddress;
644 }
645 
646 std::string
GetPlayerNameFromId(unsigned playerId) const647 ServerLobbyThread::GetPlayerNameFromId(unsigned playerId) const
648 {
649 	string name;
650 	boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByUniquePlayerId(playerId);
651 	if (!session)
652 		session = m_gameSessionManager.GetSessionByUniquePlayerId(playerId);
653 
654 	if (session && session->GetPlayerData())
655 		name = session->GetPlayerData()->GetName();
656 
657 	return name;
658 }
659 
660 void
RemovePlayer(unsigned playerId,unsigned errorCode)661 ServerLobbyThread::RemovePlayer(unsigned playerId, unsigned errorCode)
662 {
663 	m_ioService->post(boost::bind(&ServerLobbyThread::InternalRemovePlayer, shared_from_this(), playerId, errorCode));
664 }
665 
666 void
MutePlayerInGame(unsigned playerId)667 ServerLobbyThread::MutePlayerInGame(unsigned playerId)
668 {
669 	m_ioService->post(boost::bind(&ServerLobbyThread::InternalMutePlayerInGame, shared_from_this(), playerId));
670 }
671 
672 void
SendGlobalChat(const string & message)673 ServerLobbyThread::SendGlobalChat(const string &message)
674 {
675 	boost::shared_ptr<NetPacket> packet(new NetPacket);
676 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
677 	ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
678 	netChat->set_chattype(ChatMessage::chatTypeBroadcast);
679 	netChat->set_chattext(message);
680 
681 	m_sessionManager.SendToAllSessions(GetSender(), packet, SessionData::Established);
682 	m_gameSessionManager.SendToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
683 }
684 
685 void
SendGlobalMsgBox(const string & message)686 ServerLobbyThread::SendGlobalMsgBox(const string &message)
687 {
688 	boost::shared_ptr<NetPacket> packet(new NetPacket);
689 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_DialogMessage);
690 	DialogMessage *netDialog = packet->GetMsg()->mutable_dialogmessage();
691 	netDialog->set_notificationtext(message);
692 
693 	m_sessionManager.SendToAllSessions(GetSender(), packet, SessionData::Established);
694 	m_gameSessionManager.SendToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
695 }
696 
697 void
SendChatBotMsg(const std::string & message)698 ServerLobbyThread::SendChatBotMsg(const std::string &message)
699 {
700 	boost::shared_ptr<NetPacket> packet(new NetPacket);
701 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
702 	ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
703 	netChat->set_chattype(ChatMessage::chatTypeBot);
704 	netChat->set_chattext(message);
705 
706 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
707 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
708 
709 	GetIrcBotCallback().SignalLobbyMessage(
710 		0,
711 		"(chat bot)",
712 		message);
713 }
714 
715 void
SendChatBotMsg(unsigned gameId,const std::string & message)716 ServerLobbyThread::SendChatBotMsg(unsigned gameId, const std::string &message)
717 {
718 	boost::shared_ptr<NetPacket> packet(new NetPacket);
719 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
720 	ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
721 	netChat->set_chattype(ChatMessage::chatTypeBot);
722 	netChat->set_gameid(gameId);
723 	netChat->set_chattext(message);
724 
725 	GameMap::const_iterator pos = m_gameMap.find(gameId);
726 	if (pos != m_gameMap.end()) {
727 		pos->second->SendToAllPlayers(packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
728 	}
729 }
730 
731 void
ReconnectChatBot()732 ServerLobbyThread::ReconnectChatBot()
733 {
734 	m_chatCleanerManager->ReInit();
735 }
736 
737 void
AddComputerPlayer(boost::shared_ptr<PlayerData> player)738 ServerLobbyThread::AddComputerPlayer(boost::shared_ptr<PlayerData> player)
739 {
740 	boost::mutex::scoped_lock lock(m_computerPlayersMutex);
741 	m_computerPlayers.insert(PlayerDataMap::value_type(player->GetUniqueId(), player));
742 }
743 
744 void
RemoveComputerPlayer(boost::shared_ptr<PlayerData> player)745 ServerLobbyThread::RemoveComputerPlayer(boost::shared_ptr<PlayerData> player)
746 {
747 	boost::mutex::scoped_lock lock(m_computerPlayersMutex);
748 	m_computerPlayers.erase(player->GetUniqueId());
749 }
750 
751 bool
SendToLobbyPlayer(unsigned playerId,boost::shared_ptr<NetPacket> packet)752 ServerLobbyThread::SendToLobbyPlayer(unsigned playerId, boost::shared_ptr<NetPacket> packet)
753 {
754 	bool retVal = false;
755 	boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(playerId);
756 	if (tmpSession) {
757 		GetSender().Send(tmpSession, packet);
758 		retVal = true;
759 	}
760 	return retVal;
761 }
762 
763 AvatarManager &
GetAvatarManager()764 ServerLobbyThread::GetAvatarManager()
765 {
766 	return m_avatarManager;
767 }
768 
769 ChatCleanerManager &
GetChatCleaner()770 ServerLobbyThread::GetChatCleaner()
771 {
772 	assert(m_chatCleanerManager);
773 	return *m_chatCleanerManager;
774 }
775 
776 ServerStats
GetStats() const777 ServerLobbyThread::GetStats() const
778 {
779 	boost::mutex::scoped_lock lock(m_statMutex);
780 	return m_statData;
781 }
782 
783 boost::posix_time::ptime
GetStartTime() const784 ServerLobbyThread::GetStartTime() const
785 {
786 	return m_startTime;
787 }
788 
789 ServerMode
GetServerMode() const790 ServerLobbyThread::GetServerMode() const
791 {
792 	return m_mode;
793 }
794 
795 SenderHelper &
GetSender()796 ServerLobbyThread::GetSender()
797 {
798 	assert(m_sender);
799 	return *m_sender;
800 }
801 
802 boost::asio::io_service &
GetIOService()803 ServerLobbyThread::GetIOService()
804 {
805 	assert(m_ioService);
806 	return *m_ioService;
807 }
808 
809 boost::shared_ptr<ServerDBInterface>
GetDatabase()810 ServerLobbyThread::GetDatabase()
811 {
812 	return m_database;
813 }
814 
815 ServerBanManager &
GetBanManager()816 ServerLobbyThread::GetBanManager()
817 {
818 	assert(m_banManager);
819 	return *m_banManager;
820 }
821 
822 SessionDataCallback &
GetSessionDataCallback()823 ServerLobbyThread::GetSessionDataCallback()
824 {
825 	return *m_internalServerCallback;
826 }
827 
828 u_int32_t
GetNextSessionId()829 ServerLobbyThread::GetNextSessionId()
830 {
831 	return m_curSessionId++;
832 }
833 
834 u_int32_t
GetNextUniquePlayerId()835 ServerLobbyThread::GetNextUniquePlayerId()
836 {
837 	boost::mutex::scoped_lock lock(m_curUniquePlayerIdMutex);
838 	m_curUniquePlayerId++;
839 	if (m_curUniquePlayerId == 0) // 0 is an invalid id.
840 		m_curUniquePlayerId++;
841 
842 	return m_curUniquePlayerId;
843 }
844 
845 u_int32_t
GetNextGameId()846 ServerLobbyThread::GetNextGameId()
847 {
848 	m_curGameId++;
849 	if (m_curGameId == 0) // 0 is an invalid id.
850 		m_curGameId++;
851 
852 	return m_curGameId;
853 }
854 
855 void
Main()856 ServerLobbyThread::Main()
857 {
858 	try {
859 		InitAuthContext();
860 
861 		InitChatCleaner();
862 		// Start database engine.
863 		m_database->Start();
864 		// Register all timers.
865 		RegisterTimers();
866 
867 		boost::asio::io_service::work ioWork(*m_ioService);
868 		m_ioService->run(); // Will only be aborted asynchronously.
869 
870 	} catch (const PokerTHException &e) {
871 		GetCallback().SignalNetServerError(e.GetErrorId(), e.GetOsErrorCode());
872 		LOG_ERROR("Lobby exception: " << e.what());
873 	}
874 	// Clear all sessions and games.
875 	m_sessionManager.Clear();
876 	m_gameSessionManager.Clear();
877 	BOOST_FOREACH(const GameMap::value_type& tmpGame, m_gameMap) {
878 		tmpGame.second->Exit();
879 	}
880 	m_gameMap.clear();
881 	// Cancel pending timer callbacks.
882 	CancelTimers();
883 	// Stop database engine.
884 	m_database->Stop();
885 
886 	ClearAuthContext();
887 }
888 
889 void
RegisterTimers()890 ServerLobbyThread::RegisterTimers()
891 {
892 	// Remove closed games.
893 	m_removeGameTimer.expires_from_now(
894 		milliseconds(SERVER_REMOVE_GAME_INTERVAL_MSEC));
895 	m_removeGameTimer.async_wait(
896 		boost::bind(
897 			&ServerLobbyThread::TimerRemoveGame, shared_from_this(), boost::asio::placeholders::error));
898 	// Update the statistics file.
899 	m_saveStatisticsTimer.expires_from_now(
900 		seconds(SERVER_SAVE_STATISTICS_INTERVAL_SEC));
901 	m_saveStatisticsTimer.async_wait(
902 		boost::bind(
903 			&ServerLobbyThread::TimerSaveStatisticsFile, shared_from_this(), boost::asio::placeholders::error));
904 	// Update the avatar upload locks.
905 	m_loginLockTimer.expires_from_now(
906 		milliseconds(SERVER_UPDATE_LOGIN_LOCK_INTERVAL_MSEC));
907 	m_loginLockTimer.async_wait(
908 		boost::bind(
909 			&ServerLobbyThread::TimerUpdateClientLoginLock, shared_from_this(), boost::asio::placeholders::error));
910 }
911 
912 void
CancelTimers()913 ServerLobbyThread::CancelTimers()
914 {
915 	m_removeGameTimer.cancel();
916 	m_saveStatisticsTimer.cancel();
917 	m_loginLockTimer.cancel();
918 }
919 
920 void
InitAuthContext()921 ServerLobbyThread::InitAuthContext()
922 {
923 	int res = gsasl_init(&m_authContext);
924 	if (res != GSASL_OK)
925 		throw ServerException(__FILE__, __LINE__, ERR_NET_GSASL_INIT_FAILED, 0);
926 
927 	if (!gsasl_server_support_p(m_authContext, "SCRAM-SHA-1")) {
928 		gsasl_done(m_authContext);
929 		throw ServerException(__FILE__, __LINE__, ERR_NET_GSASL_NO_SCRAM, 0);
930 	}
931 }
932 
933 void
ClearAuthContext()934 ServerLobbyThread::ClearAuthContext()
935 {
936 	gsasl_done(m_authContext);
937 	m_authContext = NULL;
938 }
939 
940 void
InitChatCleaner()941 ServerLobbyThread::InitChatCleaner()
942 {
943 	if (m_serverConfig.readConfigInt("UseChatCleaner") != 0) {
944 		m_chatCleanerManager->Init(
945 			m_serverConfig.readConfigString("ChatCleanerHostAddress"),
946 			m_serverConfig.readConfigInt("ChatCleanerPort"),
947 			m_serverConfig.readConfigInt("ChatCleanerUseIpv6") != 0,
948 			m_serverConfig.readConfigString("ChatCleanerClientAuth"),
949 			m_serverConfig.readConfigString("ChatCleanerServerAuth"));
950 	}
951 }
952 
953 void
DispatchPacket(boost::shared_ptr<SessionData> session,boost::shared_ptr<NetPacket> packet)954 ServerLobbyThread::DispatchPacket(boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
955 {
956 	if (session) {
957 		// Retrieve current game, if applicable.
958 		boost::shared_ptr<ServerGame> game = session->GetGame();
959 		if (game) {
960 			// We need to catch game-specific exceptions, so that they do not affect the server.
961 			try {
962 				game->HandlePacket(session, packet);
963 			} catch (const PokerTHException &e) {
964 				LOG_ERROR("Game " << game->GetId() << " - Read handler exception: " << e.what());
965 				game->RemoveAllSessions();
966 			}
967 		} else
968 			HandlePacket(session, packet);
969 	}
970 }
971 
972 void
HandlePacket(boost::shared_ptr<SessionData> session,boost::shared_ptr<NetPacket> packet)973 ServerLobbyThread::HandlePacket(boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
974 {
975 	if (session && packet) {
976 		if (packet->IsClientActivity())
977 			session->ResetActivityTimer();
978 
979 		if (session->GetState() == SessionData::Init) {
980 			if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_InitMessage) {
981 				HandleNetPacketInit(session, packet->GetMsg()->initmessage());
982 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AuthClientResponseMessage) {
983 				HandleNetPacketAuthClientResponse(session, packet->GetMsg()->authclientresponsemessage());
984 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarHeaderMessage) {
985 				HandleNetPacketAvatarHeader(session, packet->GetMsg()->avatarheadermessage());
986 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_UnknownAvatarMessage) {
987 				HandleNetPacketUnknownAvatar(session, packet->GetMsg()->unknownavatarmessage());
988 			} else {
989 				SessionError(session, ERR_SOCK_INVALID_STATE);
990 			}
991 		} else if (session->GetState() == SessionData::ReceivingAvatar) {
992 			if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarDataMessage) {
993 				HandleNetPacketAvatarFile(session, packet->GetMsg()->avatardatamessage());
994 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarEndMessage) {
995 				HandleNetPacketAvatarEnd(session, packet->GetMsg()->avatarendmessage());
996 			} else {
997 				SessionError(session, ERR_SOCK_INVALID_STATE);
998 			}
999 		} else {
1000 			if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_PlayerInfoRequestMessage)
1001 				HandleNetPacketRetrievePlayerInfo(session, packet->GetMsg()->playerinforequestmessage());
1002 			else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarRequestMessage)
1003 				HandleNetPacketRetrieveAvatar(session, packet->GetMsg()->avatarrequestmessage());
1004 			else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ResetTimeoutMessage) {
1005 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_SubscriptionRequestMessage) {
1006 				const SubscriptionRequestMessage &subscriptionRequest = packet->GetMsg()->subscriptionrequestmessage();
1007 				if (subscriptionRequest.subscriptionaction() == SubscriptionRequestMessage::resubscribeGameList)
1008 					InternalResubscribeMsg(session);
1009 				else
1010 					session->ResetWantsLobbyMsg();
1011 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_JoinNewGameMessage) {
1012 				HandleNetPacketCreateGame(session, packet->GetMsg()->joinnewgamemessage());
1013 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_JoinExistingGameMessage) {
1014 				HandleNetPacketJoinGame(session, packet->GetMsg()->joinexistinggamemessage());
1015 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_RejoinExistingGameMessage) {
1016 				HandleNetPacketRejoinGame(session, packet->GetMsg()->rejoinexistinggamemessage());
1017 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ChatRequestMessage) {
1018 				HandleNetPacketChatRequest(session, packet->GetMsg()->chatrequestmessage());
1019 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_RejectGameInvitationMessage) {
1020 				HandleNetPacketRejectGameInvitation(session, packet->GetMsg()->rejectgameinvitationmessage());
1021 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ReportGameMessage) {
1022 				HandleNetPacketReportGame(session, packet->GetMsg()->reportgamemessage());
1023 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_LeaveGameRequestMessage) {
1024 				// Ignore "leave game" in this state.
1025 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AdminRemoveGameMessage) {
1026 				HandleNetPacketAdminRemoveGame(session, packet->GetMsg()->adminremovegamemessage());
1027 			} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AdminBanPlayerMessage) {
1028 				HandleNetPacketAdminBanPlayer(session, packet->GetMsg()->adminbanplayermessage());
1029 			} else {
1030 				SessionError(session, ERR_SOCK_INVALID_STATE);
1031 			}
1032 		}
1033 	}
1034 }
1035 
1036 void
HandleNetPacketInit(boost::shared_ptr<SessionData> session,const InitMessage & initMessage)1037 ServerLobbyThread::HandleNetPacketInit(boost::shared_ptr<SessionData> session, const InitMessage &initMessage)
1038 {
1039 	LOG_VERBOSE("Received init for session #" << session->GetId() << ".");
1040 
1041 	// Before any other processing, perform some denial of service and
1042 	// brute force attack prevention by checking whether the user recently sent an
1043 	// Init packet.
1044 	if (m_serverConfig.readConfigInt("ServerBruteForceProtection") != 0) {
1045 		bool recentlySentInit = false;
1046 		{
1047 			boost::mutex::scoped_lock lock(m_timerClientAddressMapMutex);
1048 			if (m_timerClientAddressMap.find(session->GetClientAddr()) != m_timerClientAddressMap.end())
1049 				recentlySentInit = true;
1050 			else
1051 				m_timerClientAddressMap[session->GetClientAddr()] = boost::timers::portable::microsec_timer();
1052 		}
1053 		if (recentlySentInit) {
1054 			SessionError(session, ERR_NET_INIT_BLOCKED);
1055 			return;
1056 		}
1057 	}
1058 
1059 	// Check the protocol version.
1060 	if (initMessage.requestedversion().majorversion() != NET_VERSION_MAJOR
1061 			|| session->GetPlayerData()) { // Has this session already sent an init?
1062 		SessionError(session, ERR_NET_VERSION_NOT_SUPPORTED);
1063 		return;
1064 	}
1065 #ifndef POKERTH_OFFICIAL_SERVER
1066 	// Check (clear text) server password (skip for official server, they are open to everyone).
1067 	string serverPassword;
1068 	if (initMessage.has_authserverpassword()) {
1069 		serverPassword = initMessage.authserverpassword();
1070 	}
1071 	if (serverPassword != m_serverConfig.readConfigString("ServerPassword")) {
1072 		SessionError(session, ERR_NET_INVALID_PASSWORD);
1073 		return;
1074 	}
1075 #endif
1076 
1077 	string playerName;
1078 	MD5Buf avatarMD5;
1079 	bool noAuth = false;
1080 	bool validGuest = false;
1081 	// productive: if (initMessage.login() == InitMessage::guestLogin) {
1082 	// debug: if (initMessage.login() == InitMessage::unauthenticatedLogin) {
1083 	if (initMessage.login() == InitMessage::guestLogin) {
1084 		playerName = initMessage.nickname();
1085 		// Verify guest player name.
1086 		if (playerName.length() > sizeof(SERVER_GUEST_PLAYER_NAME - 1)
1087 				&& playerName.substr(0, sizeof(SERVER_GUEST_PLAYER_NAME) - 1) == SERVER_GUEST_PLAYER_NAME) {
1088 			string guestId(playerName.substr(sizeof(SERVER_GUEST_PLAYER_NAME)));
1089 			if ((size_t)count_if(guestId.begin(), guestId.end(), ::isdigit) == guestId.size()) {
1090 				validGuest = true;
1091 				noAuth = true;
1092 			}
1093 			// check if a guest session in lobby with same ip is already connected and
1094 			// if number of lobby guests >= SERVER_MAX_GUEST_USERS_LOBBY
1095 			if(m_serverConfig.readConfigInt("ServerRestrictGuestLogin") != 0
1096 					&& !m_sessionManager.IsGuestAllowedToConnect(session->GetClientAddr())) {
1097 				SessionError(session, ERR_NET_SERVER_FULL);
1098 				return;
1099 			}
1100 		}
1101 
1102 		if (!validGuest) {
1103 			SessionError(session, ERR_NET_INVALID_PLAYER_NAME);
1104 			return;
1105 		}
1106 	}
1107 #ifdef POKERTH_OFFICIAL_SERVER
1108 	else if (initMessage.login() == InitMessage::authenticatedLogin) {
1109 		string inAuthData(initMessage.clientuserdata());
1110 		if (initMessage.has_avatarhash()) {
1111 			memcpy(avatarMD5.GetData(), initMessage.avatarhash().data(), MD5_DATA_SIZE);
1112 		}
1113 		session->CreateServerAuthSession(m_authContext);
1114 		if (session->AuthStep(1, inAuthData))
1115 			playerName = session->AuthGetUser();
1116 	}
1117 #else
1118 	else if (initMessage.login() == InitMessage::unauthenticatedLogin) {
1119 		playerName = initMessage.nickname();
1120 		if (initMessage.has_avatarhash()) {
1121 			memcpy(avatarMD5.GetData(), initMessage.avatarhash().data(), MD5_DATA_SIZE);
1122 		}
1123 		noAuth = true;
1124 	}
1125 #endif
1126 	else {
1127 		SessionError(session, ERR_NET_INVALID_PASSWORD);
1128 		return;
1129 	}
1130 
1131 	// Check whether the player name is correct.
1132 	// Partly, this is also done in netpacket.
1133 	// However, some disallowed names are checked only here.
1134 	if (playerName.empty()
1135 			|| playerName[0] == '#'
1136 			|| playerName[0] == ' '
1137 			|| playerName.substr(0, sizeof(SERVER_COMPUTER_PLAYER_NAME) - 1) == SERVER_COMPUTER_PLAYER_NAME) {
1138 		SessionError(session, ERR_NET_INVALID_PLAYER_NAME);
1139 		return;
1140 	}
1141 
1142 	// Check whether the player name is banned.
1143 	if (GetBanManager().IsPlayerBanned(playerName)) {
1144 		SessionError(session, ERR_NET_PLAYER_BANNED);
1145 		return;
1146 	}
1147 	// Check whether the peer IP address is banned.
1148 	if (GetBanManager().IsIPAddressBanned(session->GetClientAddr())) {
1149 		SessionError(session, ERR_NET_PLAYER_BANNED);
1150 		return;
1151 	}
1152 
1153 	// Create player data object.
1154 	boost::shared_ptr<PlayerData> tmpPlayerData(
1155 		new PlayerData(GetNextUniquePlayerId(), 0, PLAYER_TYPE_HUMAN, validGuest ? PLAYER_RIGHTS_GUEST : PLAYER_RIGHTS_NORMAL, false));
1156 	tmpPlayerData->SetName(playerName);
1157 	tmpPlayerData->SetAvatarMD5(avatarMD5);
1158 	if (initMessage.has_mylastsessionid()) {
1159 		tmpPlayerData->SetOldGuid(initMessage.mylastsessionid());
1160 	}
1161 
1162 	// Set player data for session.
1163 	m_sessionManager.SetSessionPlayerData(session->GetId(), tmpPlayerData);
1164 	session->SetPlayerData(tmpPlayerData);
1165 
1166 	if (noAuth)
1167 		InitAfterLogin(session);
1168 	else
1169 		AuthenticatePlayer(session);
1170 }
1171 
1172 void
HandleNetPacketAuthClientResponse(boost::shared_ptr<SessionData> session,const AuthClientResponseMessage & clientResponse)1173 ServerLobbyThread::HandleNetPacketAuthClientResponse(boost::shared_ptr<SessionData> session, const AuthClientResponseMessage &clientResponse)
1174 {
1175 	if (session && session->GetPlayerData() && session->AuthGetCurStepNum() == 1) {
1176 		string authData = clientResponse.clientresponse();
1177 		if (session->AuthStep(2, authData)) {
1178 			string outVerification(session->AuthGetNextOutMsg());
1179 
1180 			boost::shared_ptr<NetPacket> packet(new NetPacket);
1181 			packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AuthServerVerificationMessage);
1182 			AuthServerVerificationMessage *verification = packet->GetMsg()->mutable_authserververificationmessage();
1183 			verification->set_serververification(outVerification);
1184 			GetSender().Send(session, packet);
1185 			// The last message is only for server verification.
1186 			// We are done now, the user has logged in.
1187 			boost::shared_ptr<PlayerData> tmpPlayerData = session->GetPlayerData();
1188 			if (GetBanManager().IsAdminPlayer(tmpPlayerData->GetDBId())) {
1189 				session->GetPlayerData()->SetRights(PLAYER_RIGHTS_ADMIN);
1190 			}
1191 			CheckAvatarBlacklist(session);
1192 		} else
1193 			SessionError(session, ERR_NET_INVALID_PASSWORD);
1194 	}
1195 }
1196 
1197 void
HandleNetPacketAvatarHeader(boost::shared_ptr<SessionData> session,const AvatarHeaderMessage & avatarHeader)1198 ServerLobbyThread::HandleNetPacketAvatarHeader(boost::shared_ptr<SessionData> session, const AvatarHeaderMessage &avatarHeader)
1199 {
1200 	if (session->GetPlayerData()) {
1201 		if (avatarHeader.avatarsize() >= MIN_AVATAR_FILE_SIZE && avatarHeader.avatarsize() <= MAX_AVATAR_FILE_SIZE) {
1202 			boost::shared_ptr<AvatarFile> tmpAvatarFile(new AvatarFile);
1203 			tmpAvatarFile->fileData.reserve(avatarHeader.avatarsize());
1204 			tmpAvatarFile->fileType = static_cast<AvatarFileType>(avatarHeader.avatartype());
1205 			tmpAvatarFile->reportedSize = avatarHeader.avatarsize();
1206 			// Ignore request id for now.
1207 
1208 			session->GetPlayerData()->SetNetAvatarFile(tmpAvatarFile);
1209 
1210 			// Session is now receiving an avatar.
1211 			session->SetState(SessionData::ReceivingAvatar);
1212 		} else
1213 			SessionError(session, ERR_NET_AVATAR_TOO_LARGE);
1214 	}
1215 }
1216 
1217 void
HandleNetPacketUnknownAvatar(boost::shared_ptr<SessionData> session,const UnknownAvatarMessage &)1218 ServerLobbyThread::HandleNetPacketUnknownAvatar(boost::shared_ptr<SessionData> session, const UnknownAvatarMessage &/*unknownAvatar*/)
1219 {
1220 	if (session->GetPlayerData()) {
1221 		// Free memory (just in case).
1222 		session->GetPlayerData()->SetNetAvatarFile(boost::shared_ptr<AvatarFile>());
1223 		session->GetPlayerData()->SetAvatarMD5(MD5Buf());
1224 		// Start session.
1225 		EstablishSession(session);
1226 	}
1227 }
1228 
1229 void
HandleNetPacketAvatarFile(boost::shared_ptr<SessionData> session,const AvatarDataMessage & avatarData)1230 ServerLobbyThread::HandleNetPacketAvatarFile(boost::shared_ptr<SessionData> session, const AvatarDataMessage &avatarData)
1231 {
1232 	if (session->GetPlayerData()) {
1233 		boost::shared_ptr<AvatarFile> tmpAvatar = session->GetPlayerData()->GetNetAvatarFile();
1234 		const string &avatarBlock = avatarData.avatarblock();
1235 		if (tmpAvatar && tmpAvatar->fileData.size() + avatarBlock.size() <= tmpAvatar->reportedSize) {
1236 			std::copy(&avatarBlock[0], &avatarBlock[avatarBlock.size()], back_inserter(tmpAvatar->fileData));
1237 		}
1238 	}
1239 }
1240 
1241 void
HandleNetPacketAvatarEnd(boost::shared_ptr<SessionData> session,const AvatarEndMessage &)1242 ServerLobbyThread::HandleNetPacketAvatarEnd(boost::shared_ptr<SessionData> session, const AvatarEndMessage &/*avatarEnd*/)
1243 {
1244 	if (session->GetPlayerData()) {
1245 		boost::shared_ptr<AvatarFile> tmpAvatar = session->GetPlayerData()->GetNetAvatarFile();
1246 		MD5Buf avatarMD5 = session->GetPlayerData()->GetAvatarMD5();
1247 		if (!avatarMD5.IsZero() && tmpAvatar.get()) {
1248 			unsigned avatarSize = (unsigned)tmpAvatar->fileData.size();
1249 			if (avatarSize == tmpAvatar->reportedSize) {
1250 				if (!GetAvatarManager().StoreAvatarInCache(avatarMD5, tmpAvatar->fileType, &tmpAvatar->fileData[0], avatarSize, true)) {
1251 					session->GetPlayerData()->SetAvatarMD5(MD5Buf());
1252 					LOG_ERROR("Failed to store avatar in cache directory.");
1253 				}
1254 
1255 				// Free memory.
1256 				session->GetPlayerData()->SetNetAvatarFile(boost::shared_ptr<AvatarFile>());
1257 				// Set avatar file name.
1258 				string avatarFileName;
1259 				if (GetAvatarManager().GetAvatarFileName(avatarMD5, avatarFileName))
1260 					session->GetPlayerData()->SetAvatarFile(avatarFileName);
1261 				// Init finished - start session.
1262 				EstablishSession(session);
1263 				LOG_MSG("Client \"" << session->GetClientAddr() << "\" uploaded avatar \""
1264 						<< boost::filesystem::path(avatarFileName).file_string() << "\".");
1265 			} else
1266 				SessionError(session, ERR_NET_WRONG_AVATAR_SIZE);
1267 		}
1268 	}
1269 }
1270 
1271 void
HandleNetPacketRetrievePlayerInfo(boost::shared_ptr<SessionData> session,const PlayerInfoRequestMessage & playerInfoRequest)1272 ServerLobbyThread::HandleNetPacketRetrievePlayerInfo(boost::shared_ptr<SessionData> session, const PlayerInfoRequestMessage &playerInfoRequest)
1273 {
1274 	BOOST_FOREACH(unsigned playerId, playerInfoRequest.playerid()) {
1275 		// Find player in lobby or in a game.
1276 		boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(playerId);
1277 		if (!tmpSession) {
1278 			tmpSession = m_gameSessionManager.GetSessionByUniquePlayerId(playerId);
1279 		}
1280 		boost::shared_ptr<PlayerData> tmpPlayer;
1281 		if (tmpSession) {
1282 			tmpPlayer = tmpSession->GetPlayerData();
1283 		}
1284 
1285 		if (!tmpPlayer) {
1286 			boost::mutex::scoped_lock lock(m_computerPlayersMutex);
1287 			PlayerDataMap::const_iterator pos = m_computerPlayers.find(playerId);
1288 			if (pos != m_computerPlayers.end())
1289 				tmpPlayer = pos->second;
1290 		}
1291 
1292 		boost::shared_ptr<NetPacket> packet(new NetPacket);
1293 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayerInfoReplyMessage);
1294 		PlayerInfoReplyMessage *netPlayerInfoReply = packet->GetMsg()->mutable_playerinforeplymessage();
1295 		netPlayerInfoReply->set_playerid(playerId);
1296 
1297 		if (tmpPlayer) {
1298 			// Send player info to client.
1299 			PlayerInfoReplyMessage::PlayerInfoData *data = netPlayerInfoReply->mutable_playerinfodata();
1300 
1301 			data->set_playername(tmpPlayer->GetName());
1302 			data->set_ishuman(tmpPlayer->GetType() == PLAYER_TYPE_HUMAN);
1303 			data->set_playerrights(static_cast<NetPlayerInfoRights>(tmpPlayer->GetRights()));
1304 			if (!tmpPlayer->GetCountry().empty()) {
1305 				data->set_countrycode(tmpPlayer->GetCountry());
1306 			}
1307 			if (!tmpPlayer->GetAvatarMD5().IsZero()) {
1308 				PlayerInfoReplyMessage::PlayerInfoData::AvatarData *avatarData = data->mutable_avatardata();
1309 				avatarData->set_avatartype(static_cast<NetAvatarType>(AvatarManager::GetAvatarFileType(tmpPlayer->GetAvatarFile())));
1310 				avatarData->set_avatarhash(tmpPlayer->GetAvatarMD5().GetData(), MD5_DATA_SIZE);
1311 			}
1312 		} else {
1313 			// Unknown player id - do not set any data.
1314 		}
1315 		GetSender().Send(session, packet);
1316 	}
1317 }
1318 
1319 void
HandleNetPacketRetrieveAvatar(boost::shared_ptr<SessionData> session,const AvatarRequestMessage & retrieveAvatar)1320 ServerLobbyThread::HandleNetPacketRetrieveAvatar(boost::shared_ptr<SessionData> session, const AvatarRequestMessage &retrieveAvatar)
1321 {
1322 	bool avatarFound = false;
1323 
1324 	string tmpFile;
1325 	MD5Buf tmpMD5;
1326 	memcpy(tmpMD5.GetData(), retrieveAvatar.avatarhash().data(), MD5_DATA_SIZE);
1327 	if (GetAvatarManager().GetAvatarFileName(tmpMD5, tmpFile)) {
1328 		NetPacketList tmpPackets;
1329 		if (GetAvatarManager().AvatarFileToNetPackets(tmpFile, retrieveAvatar.requestid(), tmpPackets) == 0) {
1330 			avatarFound = true;
1331 			GetSender().Send(session, tmpPackets);
1332 		} else
1333 			LOG_ERROR("Failed to read avatar file for network transmission.");
1334 	}
1335 
1336 	if (!avatarFound) {
1337 		// Notify client we didn't find the avatar.
1338 		boost::shared_ptr<NetPacket> unknownAvatar(new NetPacket);
1339 		unknownAvatar->GetMsg()->set_messagetype(PokerTHMessage::Type_UnknownAvatarMessage);
1340 		UnknownAvatarMessage *netAvatarReply = unknownAvatar->GetMsg()->mutable_unknownavatarmessage();
1341 		netAvatarReply->set_requestid(retrieveAvatar.requestid());
1342 
1343 		GetSender().Send(session, unknownAvatar);
1344 	}
1345 }
1346 
1347 void
HandleNetPacketCreateGame(boost::shared_ptr<SessionData> session,const JoinNewGameMessage & newGame)1348 ServerLobbyThread::HandleNetPacketCreateGame(boost::shared_ptr<SessionData> session, const JoinNewGameMessage &newGame)
1349 {
1350 	LOG_VERBOSE("Creating new game, initiated by session #" << session->GetId() << ".");
1351 
1352 	string password;
1353 	if (newGame.has_password())
1354 		password = newGame.password();
1355 
1356 	// Create a new game.
1357 	GameData tmpData;
1358 	NetPacket::GetGameData(newGame.gameinfo(), tmpData);
1359 	string gameName(newGame.gameinfo().gamename());
1360 	// Always trim the game name.
1361 	boost::trim(gameName);
1362 	boost::replace_all(gameName, "\n", " ");
1363 	boost::replace_all(gameName, "\r", " ");
1364 	boost::replace_all(gameName, "\t", " ");
1365 	boost::replace_all(gameName, "\v", " ");
1366 	boost::replace_all(gameName, "\f", " ");
1367 	unsigned gameId = GetNextGameId();
1368 
1369 	if (gameName.empty() || !isprint(gameName[0])) {
1370 		SendJoinGameFailed(session, gameId, NTF_NET_JOIN_GAME_BAD_NAME);
1371 	} else if (IsGameNameInUse(gameName)) {
1372 		SendJoinGameFailed(session, gameId, NTF_NET_JOIN_GAME_NAME_IN_USE);
1373 	} else if (GetBanManager().IsBadGameName(gameName)) {
1374 		SendJoinGameFailed(session, gameId, NTF_NET_JOIN_GAME_BAD_NAME);
1375 	} else if (session->GetPlayerData()->GetRights() == PLAYER_RIGHTS_GUEST
1376 			   && tmpData.gameType != GAME_TYPE_NORMAL) {
1377 		SendJoinGameFailed(session, gameId, NTF_NET_JOIN_GUEST_FORBIDDEN);
1378 	} else if (!ServerGame::CheckSettings(tmpData, password, GetServerMode())) {
1379 		SendJoinGameFailed(session, gameId, NTF_NET_JOIN_INVALID_SETTINGS);
1380 	} else {
1381 		boost::shared_ptr<ServerGame> game(
1382 			new ServerGame(
1383 				shared_from_this(),
1384 				gameId,
1385 				gameName,
1386 				password,
1387 				tmpData,
1388 				session->GetPlayerData()->GetUniqueId(),
1389 				session->GetPlayerData()->GetDBId(),
1390 				GetGui(),
1391 				m_serverConfig));
1392 		game->Init();
1393 
1394 		// Add game to list of games.
1395 		InternalAddGame(game);
1396 
1397 		MoveSessionToGame(game, session, newGame.autoleave(), false);
1398 	}
1399 }
1400 
1401 void
HandleNetPacketJoinGame(boost::shared_ptr<SessionData> session,const JoinExistingGameMessage & joinGame)1402 ServerLobbyThread::HandleNetPacketJoinGame(boost::shared_ptr<SessionData> session, const JoinExistingGameMessage &joinGame)
1403 {
1404 	string password;
1405 	if (joinGame.has_password())
1406 		password = joinGame.password();
1407 
1408 	// Join an existing game.
1409 	GameMap::iterator pos = m_gameMap.find(joinGame.gameid());
1410 
1411 	if (pos != m_gameMap.end()) {
1412 		boost::shared_ptr<ServerGame> game = pos->second;
1413 		const GameData &tmpData = game->GetGameData();
1414 		if (joinGame.spectateonly()) {
1415 			if (!tmpData.allowSpectators) {
1416 				SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_NO_SPECTATORS);
1417 			} else {
1418 				MoveSessionToGame(game, session, joinGame.autoleave(), true);
1419 			}
1420 		} else {
1421 			// As guest, you are only allowed to join normal games.
1422 			if (session->GetPlayerData()->GetRights() == PLAYER_RIGHTS_GUEST
1423 					&& tmpData.gameType != GAME_TYPE_NORMAL) {
1424 				SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_GUEST_FORBIDDEN);
1425 			} else if (tmpData.gameType == GAME_TYPE_INVITE_ONLY
1426 					   && !game->IsPlayerInvited(session->GetPlayerData()->GetUniqueId())) {
1427 				SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_NOT_INVITED);
1428 			} else if (!game->CheckPassword(password)) {
1429 				SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_INVALID_PASSWORD);
1430 			} else if (tmpData.gameType == GAME_TYPE_RANKING
1431 					   && !joinGame.spectateonly()
1432 					   && session->GetClientAddr() != SERVER_ADDRESS_LOCALHOST_STR
1433 					   && session->GetClientAddr() != SERVER_ADDRESS_LOCALHOST_STR_V4V6
1434 					   && session->GetClientAddr() != SERVER_ADDRESS_LOCALHOST_STR_V4
1435 					   && game->IsClientAddressConnected(session->GetClientAddr())) {
1436 				SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_IP_BLOCKED);
1437 
1438 			} else {
1439 				MoveSessionToGame(game, session, joinGame.autoleave(), false);
1440 			}
1441 		}
1442 	} else {
1443 		SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_GAME_INVALID);
1444 	}
1445 }
1446 
1447 void
HandleNetPacketRejoinGame(boost::shared_ptr<SessionData> session,const RejoinExistingGameMessage & rejoinGame)1448 ServerLobbyThread::HandleNetPacketRejoinGame(boost::shared_ptr<SessionData> session, const RejoinExistingGameMessage &rejoinGame)
1449 {
1450 	// Rejoin a running game.
1451 	GameMap::iterator pos = m_gameMap.find(rejoinGame.gameid());
1452 
1453 	if (pos != m_gameMap.end()) {
1454 		boost::shared_ptr<ServerGame> game = pos->second;
1455 		MoveSessionToGame(game, session, rejoinGame.autoleave(), false);
1456 	} else {
1457 		SendJoinGameFailed(session, rejoinGame.gameid(), NTF_NET_JOIN_GAME_INVALID);
1458 	}
1459 }
1460 
1461 void
HandleNetPacketChatRequest(boost::shared_ptr<SessionData> session,const ChatRequestMessage & chatRequest)1462 ServerLobbyThread::HandleNetPacketChatRequest(boost::shared_ptr<SessionData> session, const ChatRequestMessage &chatRequest)
1463 {
1464 	bool chatSent = false;
1465 	// Guests are not allowed to chat.
1466 	if (session->GetPlayerData() && session->GetPlayerData()->GetRights() != PLAYER_RIGHTS_GUEST) {
1467 		if (!chatRequest.has_targetgameid() && !chatRequest.has_targetplayerid()) {
1468 			string chatMsg(chatRequest.chattext());
1469 
1470 			boost::shared_ptr<NetPacket> packet(new NetPacket);
1471 			packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
1472 			ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
1473 			netChat->set_chattype(ChatMessage::chatTypeLobby);
1474 			netChat->set_playerid(session->GetPlayerData()->GetUniqueId());
1475 			netChat->set_chattext(chatMsg);
1476 
1477 			m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
1478 			m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
1479 
1480 			// Send the message to the chat cleaner bot.
1481 			m_chatCleanerManager->HandleLobbyChatText(
1482 				session->GetPlayerData()->GetUniqueId(),
1483 				session->GetPlayerData()->GetName(),
1484 				chatMsg);
1485 			// Send the message to the irc bot.
1486 			GetIrcBotCallback().SignalLobbyMessage(
1487 				session->GetPlayerData()->GetUniqueId(),
1488 				session->GetPlayerData()->GetName(),
1489 				chatMsg);
1490 			chatSent = true;
1491 		} else if (!chatRequest.has_targetgameid() && chatRequest.has_targetplayerid()) {
1492 			boost::shared_ptr<SessionData> targetSession = m_sessionManager.GetSessionByUniquePlayerId(chatRequest.targetplayerid());
1493 			if (!targetSession)
1494 				targetSession = m_gameSessionManager.GetSessionByUniquePlayerId(chatRequest.targetplayerid());
1495 
1496 			if (targetSession && targetSession->GetPlayerData()) {
1497 				// Only allow private messages to players which are not in running games.
1498 				boost::shared_ptr<ServerGame> tmpGame = targetSession->GetGame();
1499 				if (!tmpGame || !tmpGame->IsRunning()) {
1500 					boost::shared_ptr<NetPacket> packet(new NetPacket);
1501 					packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
1502 					ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
1503 					netChat->set_chattype(ChatMessage::chatTypePrivate);
1504 					netChat->set_playerid(session->GetPlayerData()->GetUniqueId());
1505 					netChat->set_chattext(chatRequest.chattext());
1506 
1507 					GetSender().Send(targetSession, packet);
1508 					chatSent = true;
1509 				}
1510 			}
1511 		}
1512 	}
1513 	// Other chat types are not allowed in the lobby.
1514 	if (!chatSent) {
1515 		boost::shared_ptr<NetPacket> packet(new NetPacket);
1516 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatRejectMessage);
1517 		ChatRejectMessage *netReject = packet->GetMsg()->mutable_chatrejectmessage();
1518 		netReject->set_chattext(chatRequest.chattext());
1519 		GetSender().Send(session, packet);
1520 	}
1521 }
1522 
1523 void
HandleNetPacketRejectGameInvitation(boost::shared_ptr<SessionData> session,const RejectGameInvitationMessage & reject)1524 ServerLobbyThread::HandleNetPacketRejectGameInvitation(boost::shared_ptr<SessionData> session, const RejectGameInvitationMessage &reject)
1525 {
1526 	GameMap::iterator pos = m_gameMap.find(reject.gameid());
1527 
1528 	if (pos != m_gameMap.end() && session->GetPlayerData()) {
1529 		ServerGame &game = *pos->second;
1530 		unsigned tmpPlayerId = session->GetPlayerData()->GetUniqueId();
1531 		if (game.IsPlayerInvited(tmpPlayerId)) {
1532 			// If he actively rejects, he is no longer invited.
1533 			if (reject.myrejectreason() == RejectGameInvitationMessage::rejectReasonNo) {
1534 				game.RemovePlayerInvitation(tmpPlayerId);
1535 			}
1536 			// Send reject notification.
1537 			boost::shared_ptr<NetPacket> packet(new NetPacket);
1538 			packet->GetMsg()->set_messagetype(PokerTHMessage::Type_RejectInvNotifyMessage);
1539 			RejectInvNotifyMessage *netReject = packet->GetMsg()->mutable_rejectinvnotifymessage();
1540 			netReject->set_gameid(reject.gameid());
1541 			netReject->set_playerid(tmpPlayerId);
1542 			netReject->set_playerrejectreason(reject.myrejectreason());
1543 
1544 			game.SendToAllPlayers(packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
1545 		}
1546 	}
1547 }
1548 
1549 void
HandleNetPacketReportGame(boost::shared_ptr<SessionData> session,const ReportGameMessage & report)1550 ServerLobbyThread::HandleNetPacketReportGame(boost::shared_ptr<SessionData> session, const ReportGameMessage &report)
1551 {
1552 	GameMap::iterator pos = m_gameMap.find(report.reportedgameid());
1553 
1554 	if (pos != m_gameMap.end() && session->GetPlayerData()) {
1555 		boost::shared_ptr<ServerGame> tmpGame(pos->second);
1556 		if (!tmpGame->IsNameReported()) {
1557 			// Temporarily note that this game was reported.
1558 			// This prevents spamming of the game report.
1559 			tmpGame->SetNameReported();
1560 			unsigned creatorDBId = tmpGame->GetCreatorDBId();
1561 			unsigned reporterDBId = session->GetPlayerData()->GetDBId();
1562 			GetDatabase()->AsyncReportGame(
1563 				session->GetPlayerData()->GetUniqueId(),
1564 				tmpGame->GetId(),
1565 				creatorDBId != 0 ? &creatorDBId : NULL,
1566 				tmpGame->GetId(),
1567 				tmpGame->GetName(),
1568 				reporterDBId != 0 ? &reporterDBId : NULL
1569 			);
1570 		} else {
1571 			boost::shared_ptr<NetPacket> packet(new NetPacket);
1572 			packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ReportGameAckMessage);
1573 			ReportGameAckMessage *netReportAck = packet->GetMsg()->mutable_reportgameackmessage();
1574 			netReportAck->set_reportedgameid(report.reportedgameid());
1575 			netReportAck->set_reportgameresult(ReportGameAckMessage::gameReportDuplicate);
1576 			GetSender().Send(session, packet);
1577 		}
1578 	} else {
1579 		boost::shared_ptr<NetPacket> packet(new NetPacket);
1580 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ReportGameAckMessage);
1581 		ReportGameAckMessage *netReportAck = packet->GetMsg()->mutable_reportgameackmessage();
1582 		netReportAck->set_reportedgameid(report.reportedgameid());
1583 		netReportAck->set_reportgameresult(ReportGameAckMessage::gameReportInvalid);
1584 		GetSender().Send(session, packet);
1585 	}
1586 }
1587 
1588 void
HandleNetPacketAdminRemoveGame(boost::shared_ptr<SessionData> session,const AdminRemoveGameMessage & removeGame)1589 ServerLobbyThread::HandleNetPacketAdminRemoveGame(boost::shared_ptr<SessionData> session, const AdminRemoveGameMessage &removeGame)
1590 {
1591 	GameMap::iterator pos = m_gameMap.find(removeGame.removegameid());
1592 
1593 	// Create Ack-Packet.
1594 	boost::shared_ptr<NetPacket> packet(new NetPacket);
1595 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AdminRemoveGameAckMessage);
1596 	AdminRemoveGameAckMessage *netRemoveAck = packet->GetMsg()->mutable_adminremovegameackmessage();
1597 	netRemoveAck->set_removegameid(removeGame.removegameid());
1598 
1599 	// Check whether game id is valid and whether the player is an admin.
1600 	if (pos != m_gameMap.end() && session->GetPlayerData() && GetBanManager().IsAdminPlayer(session->GetPlayerData()->GetDBId())) {
1601 		LOG_ERROR("Player " << session->GetPlayerData()->GetName() << "(" << session->GetPlayerData()->GetDBId() << ") removes game '" << pos->second->GetName() << "'");
1602 		InternalRemoveGame(pos->second);
1603 		netRemoveAck->set_removegameresult(AdminRemoveGameAckMessage::gameRemoveAccepted);
1604 	} else {
1605 		netRemoveAck->set_removegameresult(AdminRemoveGameAckMessage::gameRemoveInvalid);
1606 	}
1607 	GetSender().Send(session, packet);
1608 }
1609 
1610 void
HandleNetPacketAdminBanPlayer(boost::shared_ptr<SessionData> session,const AdminBanPlayerMessage & banPlayer)1611 ServerLobbyThread::HandleNetPacketAdminBanPlayer(boost::shared_ptr<SessionData> session, const AdminBanPlayerMessage &banPlayer)
1612 {
1613 	// Create Ack-Packet.
1614 	boost::shared_ptr<NetPacket> packet(new NetPacket);
1615 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AdminBanPlayerAckMessage);
1616 	AdminBanPlayerAckMessage *netBanAck = packet->GetMsg()->mutable_adminbanplayerackmessage();
1617 	netBanAck->set_banplayerid(banPlayer.banplayerid());
1618 
1619 	if (session && session->GetPlayerData() && GetBanManager().IsAdminPlayer(session->GetPlayerData()->GetDBId())) {
1620 
1621 		boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(banPlayer.banplayerid());
1622 		if (!tmpSession) {
1623 			tmpSession = m_gameSessionManager.GetSessionByUniquePlayerId(banPlayer.banplayerid());
1624 		}
1625 		boost::shared_ptr<PlayerData> tmpPlayer;
1626 		if (tmpSession) {
1627 			tmpPlayer = tmpSession->GetPlayerData();
1628 		}
1629 		if (tmpPlayer && !GetBanManager().IsAdminPlayer(tmpPlayer->GetDBId())) {
1630 			// Ban the player's IP address for 24 hours.
1631 			GetBanManager().BanIPAddress(tmpSession->GetClientAddr(), 24);
1632 			// Kick the player.
1633 			RemovePlayer(tmpPlayer->GetUniqueId(), ERR_NET_PLAYER_KICKED);
1634 			// Permanently ban the player in the database.
1635 			if (tmpPlayer->GetDBId() != DB_ID_INVALID) {
1636 				LOG_ERROR("Player " << session->GetPlayerData()->GetName() << "(" << session->GetPlayerData()->GetDBId()
1637 						  << ") bans player " << tmpPlayer->GetName() << "(" << tmpPlayer->GetDBId()
1638 						  << ") who has IP " << tmpSession->GetClientAddr());
1639 				GetDatabase()->AsyncBlockPlayer(session->GetPlayerData()->GetUniqueId(), tmpPlayer->GetUniqueId(), tmpPlayer->GetDBId(), 0, 4);
1640 				netBanAck->set_banplayerresult(AdminBanPlayerAckMessage::banPlayerPending);
1641 			} else {
1642 				netBanAck->set_banplayerresult(AdminBanPlayerAckMessage::banPlayerNoDB);
1643 			}
1644 		} else {
1645 			netBanAck->set_banplayerresult(AdminBanPlayerAckMessage::banPlayerInvalid);
1646 		}
1647 	} else {
1648 		netBanAck->set_banplayerresult(AdminBanPlayerAckMessage::banPlayerInvalid);
1649 	}
1650 	GetSender().Send(session, packet);
1651 }
1652 
1653 void
AuthChallenge(boost::shared_ptr<SessionData> session,const string & secret)1654 ServerLobbyThread::AuthChallenge(boost::shared_ptr<SessionData> session, const string &secret)
1655 {
1656 	if (session && session->GetPlayerData() && session->AuthGetCurStepNum() == 1) {
1657 		session->AuthSetPassword(secret); // For this auth session.
1658 		string outChallenge(session->AuthGetNextOutMsg());
1659 
1660 		boost::shared_ptr<NetPacket> packet(new NetPacket);
1661 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AuthServerChallengeMessage);
1662 		AuthServerChallengeMessage *challenge = packet->GetMsg()->mutable_authserverchallengemessage();
1663 		challenge->set_serverchallenge(outChallenge);
1664 		GetSender().Send(session, packet);
1665 	}
1666 }
1667 
1668 void
CheckAvatarBlacklist(boost::shared_ptr<SessionData> session)1669 ServerLobbyThread::CheckAvatarBlacklist(boost::shared_ptr<SessionData> session)
1670 {
1671 	if (session && session->GetPlayerData()) {
1672 		const MD5Buf &avatarMD5 = session->GetPlayerData()->GetAvatarMD5();
1673 		if (!avatarMD5.IsZero())
1674 			m_database->AsyncCheckAvatarBlacklist(session->GetPlayerData()->GetUniqueId(), avatarMD5.ToString());
1675 		else
1676 			InitAfterLogin(session);
1677 	}
1678 }
1679 
1680 void
AvatarBlacklisted(unsigned playerId)1681 ServerLobbyThread::AvatarBlacklisted(unsigned playerId)
1682 {
1683 	boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(playerId, true);
1684 	if (tmpSession && tmpSession->GetPlayerData()) {
1685 		tmpSession->GetPlayerData()->SetAvatarMD5(MD5Buf()); // Reset avatar if blacklisted.
1686 		InitAfterLogin(tmpSession);
1687 	}
1688 }
1689 
1690 void
AvatarOK(unsigned playerId)1691 ServerLobbyThread::AvatarOK(unsigned playerId)
1692 {
1693 	boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(playerId, true);
1694 	InitAfterLogin(tmpSession);
1695 }
1696 
1697 void
InitAfterLogin(boost::shared_ptr<SessionData> session)1698 ServerLobbyThread::InitAfterLogin(boost::shared_ptr<SessionData> session)
1699 {
1700 	if (session && session->GetPlayerData()) {
1701 		const MD5Buf &avatarMD5 = session->GetPlayerData()->GetAvatarMD5();
1702 		string avatarFileName;
1703 		if (!avatarMD5.IsZero()
1704 				&& !GetAvatarManager().GetAvatarFileName(avatarMD5, avatarFileName)) {
1705 			RequestPlayerAvatar(session);
1706 		} else {
1707 			if (!avatarFileName.empty())
1708 				session->GetPlayerData()->SetAvatarFile(avatarFileName);
1709 			EstablishSession(session);
1710 		}
1711 	}
1712 }
1713 
1714 void
EstablishSession(boost::shared_ptr<SessionData> session)1715 ServerLobbyThread::EstablishSession(boost::shared_ptr<SessionData> session)
1716 {
1717 	if (!session->GetPlayerData())
1718 		throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_SESSION, 0);
1719 
1720 	unsigned rejoinPlayerId = 0;
1721 	u_int32_t rejoinGameId = GetRejoinGameIdForPlayer(session->GetPlayerData()->GetName(), session->GetPlayerData()->GetOldGuid(), rejoinPlayerId);
1722 	if (rejoinGameId != 0) {
1723 		// Offer rejoin, and disconnect current player with the same name.
1724 		InternalRemovePlayer(rejoinPlayerId, ERR_NET_PLAYER_NAME_IN_USE);
1725 	} else {
1726 		// Check whether this player is already connected.
1727 		unsigned previousPlayerId = GetPlayerId(session->GetPlayerData()->GetName());
1728 		if (previousPlayerId != 0 && previousPlayerId != session->GetPlayerData()->GetUniqueId()) {
1729 #ifdef POKERTH_OFFICIAL_SERVER
1730 			// If this is a login server with a websocket connection, decline connection.
1731 			if (session->GetWebData()) {
1732 				SessionError(session, ERR_NET_PLAYER_NAME_IN_USE);
1733 				return;
1734 			} else {
1735 				// If this is not a websocket connection, disconnect the already connected player.
1736 				InternalRemovePlayer(previousPlayerId, ERR_NET_PLAYER_NAME_IN_USE);
1737 			}
1738 #else
1739 			// If this is a server without password protection, close this new session and return.
1740 			SessionError(session, ERR_NET_PLAYER_NAME_IN_USE);
1741 			return;
1742 #endif
1743 		}
1744 	}
1745 
1746 	// Run postlogin for DB
1747 	string tmpAvatarHash;
1748 	string tmpAvatarType;
1749 	if (!session->GetPlayerData()->GetAvatarMD5().IsZero()) {
1750 		tmpAvatarHash = session->GetPlayerData()->GetAvatarMD5().ToString();
1751 		tmpAvatarType = AvatarManager::GetAvatarFileExtension(AvatarManager::GetAvatarFileType(session->GetPlayerData()->GetAvatarFile()));
1752 		if (!tmpAvatarType.empty())
1753 			tmpAvatarType.erase(0, 1); // Only store extension without the "."
1754 	}
1755 	m_database->PlayerPostLogin(session->GetPlayerData()->GetDBId(), tmpAvatarHash, tmpAvatarType);
1756 
1757 	// Generate a new GUID.
1758 	boost::uuids::uuid sessionGuid(m_sessionIdGenerator());
1759 	session->GetPlayerData()->SetGuid(string((char *)&sessionGuid, boost::uuids::uuid::static_size()));
1760 
1761 	// Send ACK to client.
1762 	boost::shared_ptr<NetPacket> ack(new NetPacket);
1763 	ack->GetMsg()->set_messagetype(PokerTHMessage::Type_InitAckMessage);
1764 	InitAckMessage *netInitAck = ack->GetMsg()->mutable_initackmessage();
1765 	netInitAck->set_yoursessionid(session->GetPlayerData()->GetGuid());
1766 	netInitAck->set_yourplayerid(session->GetPlayerData()->GetUniqueId());
1767 	if (rejoinGameId != 0) {
1768 		netInitAck->set_rejoingameid(rejoinGameId);
1769 	}
1770 	GetSender().Send(session, ack);
1771 
1772 	// Send the connected players list to the client.
1773 	SendPlayerList(session);
1774 	// Send the game list to the client.
1775 	SendGameList(session);
1776 
1777 	// Session is now established.
1778 	session->SetState(SessionData::Established);
1779 
1780 	{
1781 		boost::mutex::scoped_lock lock(m_statMutex);
1782 		++m_statData.totalPlayersEverLoggedIn;
1783 		m_statDataChanged = true;
1784 	}
1785 	// Notify all players.
1786 	NotifyPlayerJoinedLobby(session->GetPlayerData()->GetUniqueId());
1787 
1788 	UpdateStatisticsNumberOfPlayers();
1789 }
1790 
1791 void
AuthenticatePlayer(boost::shared_ptr<SessionData> session)1792 ServerLobbyThread::AuthenticatePlayer(boost::shared_ptr<SessionData> session)
1793 {
1794 	if(session->GetPlayerData()) {
1795 		m_database->AsyncPlayerLogin(session->GetPlayerData()->GetUniqueId(), session->GetPlayerData()->GetName());
1796 	}
1797 }
1798 
1799 void
UserValid(unsigned playerId,const DBPlayerData & dbPlayerData)1800 ServerLobbyThread::UserValid(unsigned playerId, const DBPlayerData &dbPlayerData)
1801 {
1802 	boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(playerId, true);
1803 	if (tmpSession && tmpSession->GetPlayerData()) {
1804 		tmpSession->GetPlayerData()->SetDBId(dbPlayerData.id);
1805 		tmpSession->GetPlayerData()->SetCountry(dbPlayerData.country);
1806 		this->AuthChallenge(tmpSession, dbPlayerData.secret);
1807 	}
1808 }
1809 
1810 void
UserInvalid(unsigned playerId)1811 ServerLobbyThread::UserInvalid(unsigned playerId)
1812 {
1813 	SessionError(m_sessionManager.GetSessionByUniquePlayerId(playerId, true), ERR_NET_INVALID_PASSWORD);
1814 }
1815 
1816 void
SendReportAvatarResult(unsigned byPlayerId,unsigned reportedPlayerId,bool success)1817 ServerLobbyThread::SendReportAvatarResult(unsigned byPlayerId, unsigned reportedPlayerId, bool success)
1818 {
1819 	boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByUniquePlayerId(byPlayerId);
1820 	if (!session)
1821 		session = m_gameSessionManager.GetSessionByUniquePlayerId(byPlayerId);
1822 	if (session) {
1823 		boost::shared_ptr<NetPacket> packet(new NetPacket);
1824 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ReportAvatarAckMessage);
1825 		ReportAvatarAckMessage *netReportAck = packet->GetMsg()->mutable_reportavatarackmessage();
1826 		netReportAck->set_reportedplayerid(reportedPlayerId);
1827 		netReportAck->set_reportavatarresult(success ? ReportAvatarAckMessage::avatarReportAccepted : ReportAvatarAckMessage::avatarReportInvalid);
1828 		GetSender().Send(session, packet);
1829 	}
1830 }
1831 
1832 void
SendReportGameResult(unsigned byPlayerId,unsigned reportedGameId,bool success)1833 ServerLobbyThread::SendReportGameResult(unsigned byPlayerId, unsigned reportedGameId, bool success)
1834 {
1835 	boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByUniquePlayerId(byPlayerId);
1836 	if (!session)
1837 		session = m_gameSessionManager.GetSessionByUniquePlayerId(byPlayerId);
1838 	if (session) {
1839 		boost::shared_ptr<NetPacket> packet(new NetPacket);
1840 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ReportGameAckMessage);
1841 		ReportGameAckMessage *netReportAck = packet->GetMsg()->mutable_reportgameackmessage();
1842 		netReportAck->set_reportedgameid(reportedGameId);
1843 		netReportAck->set_reportgameresult(success ? ReportGameAckMessage::gameReportAccepted : ReportGameAckMessage::gameReportInvalid);
1844 		GetSender().Send(session, packet);
1845 	}
1846 }
1847 
1848 void
SendAdminBanPlayerResult(unsigned byPlayerId,unsigned reportedPlayerId,bool success)1849 ServerLobbyThread::SendAdminBanPlayerResult(unsigned byPlayerId, unsigned reportedPlayerId, bool success)
1850 {
1851 	boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByUniquePlayerId(byPlayerId);
1852 	if (!session)
1853 		session = m_gameSessionManager.GetSessionByUniquePlayerId(byPlayerId);
1854 	if (session) {
1855 		boost::shared_ptr<NetPacket> packet(new NetPacket);
1856 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AdminBanPlayerAckMessage);
1857 		AdminBanPlayerAckMessage *netBanAck = packet->GetMsg()->mutable_adminbanplayerackmessage();
1858 		netBanAck->set_banplayerid(reportedPlayerId);
1859 		netBanAck->set_banplayerresult(success ? AdminBanPlayerAckMessage::banPlayerAccepted : AdminBanPlayerAckMessage::banPlayerDBError);
1860 		GetSender().Send(session, packet);
1861 	}
1862 }
1863 
1864 void
UserBlocked(unsigned playerId)1865 ServerLobbyThread::UserBlocked(unsigned playerId)
1866 {
1867 	SessionError(m_sessionManager.GetSessionByUniquePlayerId(playerId, true), ERR_NET_PLAYER_BLOCKED);
1868 }
1869 
1870 void
RequestPlayerAvatar(boost::shared_ptr<SessionData> session)1871 ServerLobbyThread::RequestPlayerAvatar(boost::shared_ptr<SessionData> session)
1872 {
1873 	if (!session->GetPlayerData())
1874 		throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_SESSION, 0);
1875 	// Ask the client to send its avatar.
1876 	boost::shared_ptr<NetPacket> packet(new NetPacket);
1877 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AvatarRequestMessage);
1878 	AvatarRequestMessage *netAvatarRequest = packet->GetMsg()->mutable_avatarrequestmessage();
1879 	netAvatarRequest->set_requestid(session->GetPlayerData()->GetUniqueId());
1880 	netAvatarRequest->set_avatarhash(session->GetPlayerData()->GetAvatarMD5().GetData(), MD5_DATA_SIZE);
1881 
1882 	GetSender().Send(session, packet);
1883 }
1884 
1885 void
TimerRemoveGame(const boost::system::error_code & ec)1886 ServerLobbyThread::TimerRemoveGame(const boost::system::error_code &ec)
1887 {
1888 	if (!ec) {
1889 		// Synchronously remove games which have been closed.
1890 		GameMap::iterator i = m_gameMap.begin();
1891 		GameMap::iterator end = m_gameMap.end();
1892 		while (i != end) {
1893 			GameMap::iterator next = i;
1894 			++next;
1895 			boost::shared_ptr<ServerGame> tmpGame = i->second;
1896 			if (!tmpGame->GetSessionManager().HasSessionWithState(SessionData::Game)) {
1897 				tmpGame->MoveSpectatorsToLobby();
1898 				InternalRemoveGame(tmpGame); // This will delete the game.
1899 			}
1900 			i = next;
1901 		}
1902 		// Restart timer
1903 		m_removeGameTimer.expires_from_now(
1904 			milliseconds(SERVER_REMOVE_GAME_INTERVAL_MSEC));
1905 		m_removeGameTimer.async_wait(
1906 			boost::bind(
1907 				&ServerLobbyThread::TimerRemoveGame, shared_from_this(), boost::asio::placeholders::error));
1908 	}
1909 }
1910 
1911 void
TimerUpdateClientLoginLock(const boost::system::error_code & ec)1912 ServerLobbyThread::TimerUpdateClientLoginLock(const boost::system::error_code &ec)
1913 {
1914 	if (!ec) {
1915 		boost::mutex::scoped_lock lock(m_timerClientAddressMapMutex);
1916 
1917 		TimerClientAddressMap::iterator i = m_timerClientAddressMap.begin();
1918 		TimerClientAddressMap::iterator end = m_timerClientAddressMap.end();
1919 
1920 		while (i != end) {
1921 			TimerClientAddressMap::iterator next = i;
1922 			++next;
1923 			if (i->second.elapsed().total_seconds() > (int)SERVER_INIT_LOGIN_CLIENT_LOCK_SEC)
1924 				m_timerClientAddressMap.erase(i);
1925 			i = next;
1926 		}
1927 		// Restart timer
1928 		m_loginLockTimer.expires_from_now(
1929 			milliseconds(SERVER_UPDATE_LOGIN_LOCK_INTERVAL_MSEC));
1930 		m_loginLockTimer.async_wait(
1931 			boost::bind(
1932 				&ServerLobbyThread::TimerUpdateClientLoginLock, shared_from_this(), boost::asio::placeholders::error));
1933 	}
1934 }
1935 
1936 bool
IsGameNameInUse(const std::string & gameName) const1937 ServerLobbyThread::IsGameNameInUse(const std::string &gameName) const
1938 {
1939 	bool found = false;
1940 	GameMap::const_iterator i = m_gameMap.begin();
1941 	GameMap::const_iterator end = m_gameMap.end();
1942 
1943 	while (i != end) {
1944 		if ((*i).second->GetName() == gameName) {
1945 			found = true;
1946 			break;
1947 		}
1948 		++i;
1949 	}
1950 	return found;
1951 }
1952 
1953 boost::shared_ptr<ServerGame>
InternalGetGameFromId(unsigned gameId)1954 ServerLobbyThread::InternalGetGameFromId(unsigned gameId)
1955 {
1956 	boost::shared_ptr<ServerGame> game;
1957 	if (gameId) {
1958 		GameMap::iterator pos = m_gameMap.find(gameId);
1959 
1960 		if (pos != m_gameMap.end())
1961 			game = pos->second;
1962 	}
1963 	return game;
1964 }
1965 
1966 void
InternalAddGame(boost::shared_ptr<ServerGame> game)1967 ServerLobbyThread::InternalAddGame(boost::shared_ptr<ServerGame> game)
1968 {
1969 	// Add game to list.
1970 	m_gameMap.insert(GameMap::value_type(game->GetId(), game));
1971 	// Notify all players.
1972 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), CreateNetPacketGameListNew(*game), SessionData::Established);
1973 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), CreateNetPacketGameListNew(*game), SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
1974 
1975 	{
1976 		boost::mutex::scoped_lock lock(m_statMutex);
1977 		++m_statData.totalGamesEverCreated;
1978 		++m_statData.numberOfGamesOpen;
1979 		unsigned numGames = static_cast<unsigned>(m_gameMap.size());
1980 		if (numGames > m_statData.maxGamesOpen)
1981 			m_statData.maxGamesOpen = numGames;
1982 		m_statDataChanged = true;
1983 	}
1984 }
1985 
1986 void
InternalRemoveGame(boost::shared_ptr<ServerGame> game)1987 ServerLobbyThread::InternalRemoveGame(boost::shared_ptr<ServerGame> game)
1988 {
1989 	{
1990 		boost::mutex::scoped_lock lock(m_statMutex);
1991 		if (m_statData.numberOfGamesOpen) {
1992 			--m_statData.numberOfGamesOpen;
1993 			m_statDataChanged = true;
1994 		}
1995 	}
1996 	// Remove game from list.
1997 	m_gameMap.erase(game->GetId());
1998 	// Remove all sessions left in the game.
1999 	game->ResetComputerPlayerList();
2000 	game->RemoveAllSessions();
2001 	game->Exit();
2002 	// Notify all players.
2003 	boost::shared_ptr<NetPacket> packet = CreateNetPacketGameListUpdate(game->GetId(), GAME_MODE_CLOSED);
2004 	m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
2005 	m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
2006 }
2007 
2008 void
InternalRemovePlayer(unsigned playerId,unsigned errorCode)2009 ServerLobbyThread::InternalRemovePlayer(unsigned playerId, unsigned errorCode)
2010 {
2011 	boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByUniquePlayerId(playerId, true);
2012 	if (session)
2013 		SessionError(session, errorCode);
2014 	else {
2015 		// Remove player from game.
2016 		boost::shared_ptr<SessionData> session = m_gameSessionManager.GetSessionByUniquePlayerId(playerId);
2017 		if (session) {
2018 			boost::shared_ptr<ServerGame> tmpGame = session->GetGame();
2019 			if (tmpGame) {
2020 				tmpGame->RemovePlayer(playerId, errorCode);
2021 			}
2022 		}
2023 	}
2024 }
2025 
2026 void
InternalMutePlayerInGame(unsigned playerId)2027 ServerLobbyThread::InternalMutePlayerInGame(unsigned playerId)
2028 {
2029 	boost::shared_ptr<SessionData> session = m_gameSessionManager.GetSessionByUniquePlayerId(playerId);
2030 	if (session) {
2031 		boost::shared_ptr<ServerGame> tmpGame = session->GetGame();
2032 		if (tmpGame) {
2033 			tmpGame->MutePlayer(playerId, true);
2034 		}
2035 	}
2036 }
2037 
2038 void
InternalResubscribeMsg(boost::shared_ptr<SessionData> session)2039 ServerLobbyThread::InternalResubscribeMsg(boost::shared_ptr<SessionData> session)
2040 {
2041 	if (!session->WantsLobbyMsg()) {
2042 		session->SetWantsLobbyMsg();
2043 		SendPlayerList(session);
2044 		SendGameList(session);
2045 		// Send new statistics information.
2046 		/*		boost::shared_ptr<NetPacket> packet(new NetPacket(NetPacket::Alloc));
2047 				packet->GetMsg()->present = PokerTHMessage_PR_statisticsMessage;
2048 				StatisticsMessage_t *netStatistics = &packet->GetMsg()->choice.statisticsMessage;
2049 
2050 				StatisticsData_t *data = (StatisticsData_t *)calloc(1, sizeof(struct StatisticsData));
2051 				data->statisticsType = statisticsType_statNumberOfPlayers;
2052 				data->statisticsValue = m_sessionManager.GetRawSessionCount() + m_gameSessionManager.GetRawSessionCount();
2053 				ASN_SEQUENCE_ADD(&netStatistics->statisticsData.list, data);
2054 
2055 				GetSender().Send(session, packet);*/
2056 	}
2057 }
2058 
2059 void
HandleReAddedSession(boost::shared_ptr<SessionData> session)2060 ServerLobbyThread::HandleReAddedSession(boost::shared_ptr<SessionData> session)
2061 {
2062 	// Remove session from game session list.
2063 	m_gameSessionManager.RemoveSession(session->GetId());
2064 
2065 	if (m_sessionManager.GetRawSessionCount() <= SERVER_MAX_NUM_LOBBY_SESSIONS) {
2066 		// Set state (back) to established.
2067 		session->SetState(SessionData::Established);
2068 		session->SetGame(boost::shared_ptr<ServerGame>());
2069 		// Add session to lobby list.
2070 		m_sessionManager.AddSession(session);
2071 	} else {
2072 		// Gracefully close this session.
2073 		SessionError(session, ERR_NET_SERVER_FULL);
2074 	}
2075 }
2076 
2077 void
SessionTimeoutWarning(boost::shared_ptr<SessionData> session,unsigned remainingSec)2078 ServerLobbyThread::SessionTimeoutWarning(boost::shared_ptr<SessionData> session, unsigned remainingSec)
2079 {
2080 	boost::shared_ptr<NetPacket> packet(new NetPacket);
2081 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_TimeoutWarningMessage);
2082 	TimeoutWarningMessage *netWarning = packet->GetMsg()->mutable_timeoutwarningmessage();
2083 	netWarning->set_timeoutreason(TimeoutWarningMessage::timeoutNoDataReceived);
2084 	netWarning->set_remainingseconds(remainingSec);
2085 	GetSender().Send(session, packet);
2086 
2087 	if (session->GetGame() && session->GetPlayerData()) {
2088 		session->GetGame()->MarkPlayerAsInactive(session->GetPlayerData()->GetUniqueId());
2089 	}
2090 }
2091 
2092 void
SessionError(boost::shared_ptr<SessionData> session,int errorCode)2093 ServerLobbyThread::SessionError(boost::shared_ptr<SessionData> session, int errorCode)
2094 {
2095 	if (session) {
2096 		if (errorCode == ERR_NET_PLAYER_KICKED || errorCode == ERR_NET_SESSION_TIMED_OUT) {
2097 			if (session->GetGame() && session->GetPlayerData()) {
2098 				session->GetGame()->MarkPlayerAsKicked(session->GetPlayerData()->GetUniqueId());
2099 			}
2100 		}
2101 
2102 		SendError(session, errorCode);
2103 		CloseSession(session);
2104 	}
2105 }
2106 
2107 void
SendError(boost::shared_ptr<SessionData> s,int errorCode)2108 ServerLobbyThread::SendError(boost::shared_ptr<SessionData> s, int errorCode)
2109 {
2110 	LOG_VERBOSE("Sending error code " << errorCode << " to session #" << s->GetId() << ".");
2111 	boost::shared_ptr<NetPacket> packet(new NetPacket);
2112 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ErrorMessage);
2113 	ErrorMessage *netError = packet->GetMsg()->mutable_errormessage();
2114 	netError->set_errorreason(NetPacket::GameErrorToNetError(errorCode));
2115 	GetSender().Send(s, packet);
2116 }
2117 
2118 void
SendJoinGameFailed(boost::shared_ptr<SessionData> s,unsigned gameId,int reason)2119 ServerLobbyThread::SendJoinGameFailed(boost::shared_ptr<SessionData> s, unsigned gameId, int reason)
2120 {
2121 	boost::shared_ptr<NetPacket> packet(new NetPacket);
2122 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_JoinGameFailedMessage);
2123 	JoinGameFailedMessage *netJoinFailed = packet->GetMsg()->mutable_joingamefailedmessage();
2124 	netJoinFailed->set_gameid(gameId);
2125 
2126 	switch (reason) {
2127 	case NTF_NET_JOIN_GAME_FULL :
2128 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::gameIsFull);
2129 		break;
2130 	case NTF_NET_JOIN_ALREADY_RUNNING :
2131 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::gameIsRunning);
2132 		break;
2133 	case NTF_NET_JOIN_INVALID_PASSWORD :
2134 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::invalidPassword);
2135 		break;
2136 	case NTF_NET_JOIN_GUEST_FORBIDDEN :
2137 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::notAllowedAsGuest);
2138 		break;
2139 	case NTF_NET_JOIN_NOT_INVITED :
2140 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::notInvited);
2141 		break;
2142 	case NTF_NET_JOIN_GAME_NAME_IN_USE :
2143 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::gameNameInUse);
2144 		break;
2145 	case NTF_NET_JOIN_GAME_BAD_NAME :
2146 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::badGameName);
2147 		break;
2148 	case NTF_NET_JOIN_INVALID_SETTINGS :
2149 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::invalidSettings);
2150 		break;
2151 	case NTF_NET_JOIN_IP_BLOCKED :
2152 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::ipAddressBlocked);
2153 		break;
2154 	case NTF_NET_JOIN_REJOIN_FAILED :
2155 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::rejoinFailed);
2156 		break;
2157 	case NTF_NET_JOIN_NO_SPECTATORS :
2158 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::noSpectatorsAllowed);
2159 		break;
2160 	default :
2161 		netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::invalidGame);
2162 		break;
2163 	}
2164 	GetSender().Send(s, packet);
2165 }
2166 
2167 void
SendPlayerList(boost::shared_ptr<SessionData> s)2168 ServerLobbyThread::SendPlayerList(boost::shared_ptr<SessionData> s)
2169 {
2170 	// Retrieve all player ids.
2171 	PlayerIdList idList(m_sessionManager.GetPlayerIdList(SessionData::Established));
2172 	PlayerIdList gameIdList(m_gameSessionManager.GetPlayerIdList(SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting));
2173 	idList.splice(idList.begin(), gameIdList);
2174 	// Send all player ids to client.
2175 	PlayerIdList::const_iterator i = idList.begin();
2176 	PlayerIdList::const_iterator end = idList.end();
2177 	while (i != end) {
2178 		GetSender().Send(s, CreateNetPacketPlayerListNew(*i));
2179 		++i;
2180 	}
2181 }
2182 
2183 void
SendGameList(boost::shared_ptr<SessionData> s)2184 ServerLobbyThread::SendGameList(boost::shared_ptr<SessionData> s)
2185 {
2186 	GameMap::const_iterator game_i = m_gameMap.begin();
2187 	GameMap::const_iterator game_end = m_gameMap.end();
2188 	while (game_i != game_end) {
2189 		GetSender().Send(s, CreateNetPacketGameListNew(*game_i->second));
2190 		++game_i;
2191 	}
2192 }
2193 
2194 void
UpdateStatisticsNumberOfPlayers()2195 ServerLobbyThread::UpdateStatisticsNumberOfPlayers()
2196 {
2197 	ServerStats stats;
2198 	// Get all logged-in sessions and all sessions within a game.
2199 	unsigned curNumberOfPlayersOnServer = m_sessionManager.GetSessionCountWithState(SessionData::Established) + m_gameSessionManager.GetRawSessionCount();
2200 	{
2201 		boost::mutex::scoped_lock lock(m_statMutex);
2202 		if (curNumberOfPlayersOnServer != m_statData.numberOfPlayersOnServer) {
2203 			m_statData.numberOfPlayersOnServer = stats.numberOfPlayersOnServer = curNumberOfPlayersOnServer;
2204 			if (curNumberOfPlayersOnServer > m_statData.maxPlayersLoggedIn)
2205 				m_statData.maxPlayersLoggedIn = curNumberOfPlayersOnServer;
2206 			m_statDataChanged = true;
2207 		}
2208 	}
2209 	// Do not send other stats than number of players for now.
2210 	//BroadcastStatisticsUpdate(stats);
2211 }
2212 
2213 void
BroadcastStatisticsUpdate(const ServerStats & stats)2214 ServerLobbyThread::BroadcastStatisticsUpdate(const ServerStats &stats)
2215 {
2216 	if (stats.numberOfPlayersOnServer) {
2217 		boost::shared_ptr<NetPacket> packet(new NetPacket);
2218 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_StatisticsMessage);
2219 		StatisticsMessage *netStatistics = packet->GetMsg()->mutable_statisticsmessage();
2220 
2221 		StatisticsMessage::StatisticsData *data = netStatistics->add_statisticsdata();
2222 		data->set_statisticstype(StatisticsMessage::StatisticsData::statNumberOfPlayers);
2223 		data->set_statisticsvalue(m_sessionManager.GetRawSessionCount() + m_gameSessionManager.GetRawSessionCount());
2224 
2225 		m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
2226 		m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
2227 	}
2228 }
2229 
2230 void
ReadStatisticsFile()2231 ServerLobbyThread::ReadStatisticsFile()
2232 {
2233 	ifstream i(m_statisticsFileName.c_str(), ios_base::in);
2234 
2235 	if (!i.fail() && !i.eof()) {
2236 		boost::mutex::scoped_lock lock(m_statMutex);
2237 		do {
2238 			string statisticsType;
2239 			unsigned statisticsValue;
2240 			i >> statisticsType;
2241 			i >> statisticsValue;
2242 			if (statisticsType == SERVER_STATISTICS_STR_TOTAL_PLAYERS)
2243 				m_statData.totalPlayersEverLoggedIn = statisticsValue;
2244 			else if (statisticsType == SERVER_STATISTICS_STR_TOTAL_GAMES)
2245 				m_statData.totalGamesEverCreated = statisticsValue;
2246 			else if (statisticsType == SERVER_STATISTICS_STR_MAX_PLAYERS)
2247 				m_statData.maxPlayersLoggedIn = statisticsValue;
2248 			else if (statisticsType == SERVER_STATISTICS_STR_MAX_GAMES)
2249 				m_statData.maxGamesOpen = statisticsValue;
2250 			// other statistics are non-persistant and not read.
2251 		} while (!i.fail() && !i.eof());
2252 		m_statDataChanged = false;
2253 	}
2254 }
2255 
2256 void
TimerSaveStatisticsFile(const boost::system::error_code & ec)2257 ServerLobbyThread::TimerSaveStatisticsFile(const boost::system::error_code &ec)
2258 {
2259 	if (!ec) {
2260 		LOG_VERBOSE("Saving statistics.");
2261 		boost::mutex::scoped_lock lock(m_statMutex);
2262 		if (m_statDataChanged) {
2263 			ofstream o(m_statisticsFileName.c_str(), ios_base::out | ios_base::trunc);
2264 			if (!o.fail()) {
2265 				o << SERVER_STATISTICS_STR_TOTAL_PLAYERS " " << m_statData.totalPlayersEverLoggedIn << endl;
2266 				o << SERVER_STATISTICS_STR_TOTAL_GAMES " " << m_statData.totalGamesEverCreated << endl;
2267 				o << SERVER_STATISTICS_STR_MAX_PLAYERS " " << m_statData.maxPlayersLoggedIn << endl;
2268 				o << SERVER_STATISTICS_STR_MAX_GAMES " " << m_statData.maxGamesOpen << endl;
2269 				o << SERVER_STATISTICS_STR_CUR_PLAYERS " " << m_statData.numberOfPlayersOnServer << endl;
2270 				o << SERVER_STATISTICS_STR_CUR_GAMES " " << m_statData.numberOfGamesOpen << endl;
2271 				m_statDataChanged = false;
2272 			}
2273 		}
2274 		// Restart timer
2275 		m_saveStatisticsTimer.expires_from_now(
2276 			seconds(SERVER_SAVE_STATISTICS_INTERVAL_SEC));
2277 		m_saveStatisticsTimer.async_wait(
2278 			boost::bind(
2279 				&ServerLobbyThread::TimerSaveStatisticsFile, shared_from_this(), boost::asio::placeholders::error));
2280 	}
2281 }
2282 
2283 ServerCallback &
GetCallback()2284 ServerLobbyThread::GetCallback()
2285 {
2286 	return m_gui;
2287 }
2288 
2289 ServerIrcBotCallback &
GetIrcBotCallback()2290 ServerLobbyThread::GetIrcBotCallback()
2291 {
2292 	return m_ircBotCb;
2293 }
2294 
2295 InternalServerCallback &
GetSenderCallback()2296 ServerLobbyThread::GetSenderCallback()
2297 {
2298 	assert(m_internalServerCallback.get());
2299 	return *m_internalServerCallback;
2300 }
2301 
2302 GuiInterface &
GetGui()2303 ServerLobbyThread::GetGui()
2304 {
2305 	return m_gui;
2306 }
2307 
2308 unsigned
GetPlayerId(const string & name) const2309 ServerLobbyThread::GetPlayerId(const string &name) const
2310 {
2311 	unsigned playerId = 0;
2312 
2313 	boost::shared_ptr<SessionData> tmpSession(m_sessionManager.GetSessionByPlayerName(name));
2314 
2315 	if (!tmpSession)
2316 		tmpSession = m_gameSessionManager.GetSessionByPlayerName(name);
2317 
2318 	if (tmpSession && tmpSession->GetPlayerData())
2319 		playerId = tmpSession->GetPlayerData()->GetUniqueId();
2320 
2321 	return playerId;
2322 }
2323 
2324 boost::shared_ptr<NetPacket>
CreateNetPacketPlayerListNew(unsigned playerId)2325 ServerLobbyThread::CreateNetPacketPlayerListNew(unsigned playerId)
2326 {
2327 	boost::shared_ptr<NetPacket> packet(new NetPacket);
2328 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayerListMessage);
2329 	PlayerListMessage *netPlayerList = packet->GetMsg()->mutable_playerlistmessage();
2330 	netPlayerList->set_playerid(playerId);
2331 	netPlayerList->set_playerlistnotification(PlayerListMessage::playerListNew);
2332 	return packet;
2333 }
2334 
2335 boost::shared_ptr<NetPacket>
CreateNetPacketPlayerListLeft(unsigned playerId)2336 ServerLobbyThread::CreateNetPacketPlayerListLeft(unsigned playerId)
2337 {
2338 	boost::shared_ptr<NetPacket> packet(new NetPacket);
2339 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayerListMessage);
2340 	PlayerListMessage *netPlayerList = packet->GetMsg()->mutable_playerlistmessage();
2341 	netPlayerList->set_playerid(playerId);
2342 	netPlayerList->set_playerlistnotification(PlayerListMessage::playerListLeft);
2343 	return packet;
2344 }
2345 
2346 boost::shared_ptr<NetPacket>
CreateNetPacketGameListNew(const ServerGame & game)2347 ServerLobbyThread::CreateNetPacketGameListNew(const ServerGame &game)
2348 {
2349 	boost::shared_ptr<NetPacket> packet(new NetPacket);
2350 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListNewMessage);
2351 	GameListNewMessage *netGameList = packet->GetMsg()->mutable_gamelistnewmessage();
2352 	netGameList->set_gameid(game.GetId());
2353 	netGameList->set_adminplayerid(game.GetAdminPlayerId());
2354 	netGameList->set_gamemode(game.IsRunning() ? netGameStarted : netGameCreated);
2355 	NetPacket::SetGameData(game.GetGameData(), *netGameList->mutable_gameinfo());
2356 	netGameList->mutable_gameinfo()->set_gamename(game.GetName());
2357 	netGameList->set_isprivate(game.IsPasswordProtected());
2358 
2359 	PlayerIdList tmpList = game.GetPlayerIdList();
2360 	PlayerIdList::const_iterator i = tmpList.begin();
2361 	PlayerIdList::const_iterator end = tmpList.end();
2362 	while (i != end) {
2363 		netGameList->add_playerids(*i);
2364 		++i;
2365 	}
2366 
2367 	tmpList = game.GetSpectatorIdList();
2368 	i = tmpList.begin();
2369 	end = tmpList.end();
2370 	while (i != end) {
2371 		netGameList->add_spectatorids(*i);
2372 		++i;
2373 	}
2374 
2375 	return packet;
2376 }
2377 
2378 boost::shared_ptr<NetPacket>
CreateNetPacketGameListUpdate(unsigned gameId,GameMode mode)2379 ServerLobbyThread::CreateNetPacketGameListUpdate(unsigned gameId, GameMode mode)
2380 {
2381 	boost::shared_ptr<NetPacket> packet(new NetPacket);
2382 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListUpdateMessage);
2383 	GameListUpdateMessage *netGameList = packet->GetMsg()->mutable_gamelistupdatemessage();
2384 	netGameList->set_gameid(gameId);
2385 	netGameList->set_gamemode(static_cast<NetGameMode>(mode));
2386 	return packet;
2387 }
2388 
2389 u_int32_t
GetRejoinGameIdForPlayer(const std::string & playerName,const std::string & guid,unsigned & outPlayerUniqueId)2390 ServerLobbyThread::GetRejoinGameIdForPlayer(const std::string &playerName, const std::string &guid, unsigned &outPlayerUniqueId)
2391 {
2392 	u_int32_t retGameId = 0;
2393 	if (!guid.empty()) {
2394 		GameMap::iterator i = m_gameMap.begin();
2395 		GameMap::iterator end = m_gameMap.end();
2396 		while (i != end) {
2397 			boost::shared_ptr<ServerGame> tmpGame = i->second;
2398 			boost::shared_ptr<PlayerInterface> tmpPlayer = tmpGame->GetPlayerInterfaceFromGame(playerName);
2399 			if (tmpPlayer && tmpPlayer->getMyGuid() == guid && tmpPlayer->getMyCash() > 0) {
2400 				retGameId = tmpGame->GetId();
2401 				outPlayerUniqueId = tmpPlayer->getMyUniqueID();
2402 				break;
2403 			}
2404 			++i;
2405 		}
2406 	}
2407 	return retGameId;
2408 }
2409