Files
pokerth/src/net/common/serverlobbythread.cpp
T

2185 lines
77 KiB
C++
Raw Normal View History

2011-07-02 14:38:06 +00:00
/*****************************************************************************
* PokerTH - The open source texas holdem engine *
* Copyright (C) 2006-2012 Felix Hammer, Florian Thauer, Lothar May *
2011-07-02 14:38:06 +00:00
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
* *
* Additional permission under GNU AGPL version 3 section 7 *
* *
* If you modify this program, or any covered work, by linking or *
* combining it with the OpenSSL project's OpenSSL library (or a *
* modified version of that library), containing parts covered by the *
* terms of the OpenSSL or SSLeay licenses, the authors of PokerTH *
* (Felix Hammer, Florian Thauer, Lothar May) grant you additional *
* permission to convey the resulting work. *
* Corresponding Source for a non-source form of such a combination *
* shall include the source code for the parts of OpenSSL used as well *
* as that of the covered work. *
2011-07-02 14:38:06 +00:00
*****************************************************************************/
#include <net/serverlobbythread.h>
2009-05-31 19:45:20 +00:00
#include <net/servergame.h>
#include <net/serverbanmanager.h>
#include <net/serverexception.h>
#include <net/receivebuffer.h>
2009-05-10 22:21:20 +00:00
#include <net/senderhelper.h>
#include <net/serverircbotcallback.h>
#include <net/socket_msg.h>
2009-08-14 22:05:01 +00:00
#include <net/chatcleanermanager.h>
2010-08-01 19:23:08 +00:00
#include <net/net_helper.h>
2009-09-19 20:28:30 +00:00
#include <db/serverdbinterface.h>
#ifdef POKERTH_OFFICIAL_SERVER
#include <dbclosed/serverdbfactoryinternal.h>
2009-09-19 20:28:30 +00:00
#else
#include <db/serverdbfactorygeneric.h>
2009-09-19 20:28:30 +00:00
#endif
#include <core/avatarmanager.h>
#include <core/loghelper.h>
#include <core/openssl_wrapper.h>
2009-08-14 22:05:01 +00:00
#include <configfile.h>
#include <playerinterface.h>
#include <fstream>
#include <sstream>
#include <algorithm>
2009-11-15 08:52:17 +00:00
#include <cctype>
#include <boost/lambda/lambda.hpp>
#include <boost/filesystem.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/algorithm/string.hpp>
#include <gsasl.h>
#define SERVER_MAX_NUM_LOBBY_SESSIONS 512 // Maximum number of idle users in lobby.
#define SERVER_MAX_NUM_TOTAL_SESSIONS 2000 // Total maximum of sessions, fitting a 2048 handle limit
2009-05-10 14:57:51 +00:00
#define SERVER_SAVE_STATISTICS_INTERVAL_SEC 60
2009-05-10 14:57:51 +00:00
#define SERVER_CHECK_SESSION_TIMEOUTS_INTERVAL_MSEC 500
#define SERVER_REMOVE_GAME_INTERVAL_MSEC 500
2009-05-10 14:57:51 +00:00
#define SERVER_REMOVE_PLAYER_INTERVAL_MSEC 100
#define SERVER_UPDATE_LOGIN_LOCK_INTERVAL_MSEC 1000
#define SERVER_PROCESS_SEND_INTERVAL_MSEC 10
2009-05-10 14:57:51 +00:00
#define SERVER_INIT_LOGIN_CLIENT_LOCK_SEC NetHelper::GetLoginLockSec()
#define SERVER_INIT_SESSION_TIMEOUT_SEC 60
#define SERVER_TIMEOUT_WARNING_REMAINING_SEC 60
#define SERVER_SESSION_ACTIVITY_TIMEOUT_SEC 1800 // 30 min, MUST be > SERVER_TIMEOUT_WARNING_REMAINING_SEC
2008-03-09 23:32:45 +00:00
#define SERVER_SESSION_FORCED_TIMEOUT_SEC 86400 // 1 day, should be quite large.
2011-02-06 14:56:08 +00:00
#define SERVER_ADDRESS_LOCALHOST_STR_V4 "127.0.0.1"
#define SERVER_ADDRESS_LOCALHOST_STR_V4V6 "::ffff:127.0.0.1"
#define SERVER_ADDRESS_LOCALHOST_STR "::1"
#define SERVER_STATISTICS_FILE_NAME "server_statistics.log"
#define SERVER_STATISTICS_STR_TOTAL_PLAYERS "TotalNumPlayersLoggedIn"
#define SERVER_STATISTICS_STR_TOTAL_GAMES "TotalNumGamesCreated"
#define SERVER_STATISTICS_STR_MAX_GAMES "MaxGamesOpen"
#define SERVER_STATISTICS_STR_MAX_PLAYERS "MaxPlayersLoggedIn"
2008-04-01 21:44:05 +00:00
#define SERVER_STATISTICS_STR_CUR_GAMES "CurGamesOpen"
#define SERVER_STATISTICS_STR_CUR_PLAYERS "CurPlayersLoggedIn"
using namespace std;
using boost::asio::ip::tcp;
class InternalServerCallback : public SessionDataCallback, public ChatCleanerCallback, public ServerDBCallback
{
public:
2009-09-19 20:28:30 +00:00
InternalServerCallback(ServerLobbyThread &server) : m_server(server) {}
virtual ~InternalServerCallback() {}
virtual void CloseSession(boost::shared_ptr<SessionData> session) {
m_server.CloseSession(session);
}
virtual void SessionError(boost::shared_ptr<SessionData> session, int errorCode) {
m_server.SessionError(session, errorCode);
}
virtual void SessionTimeoutWarning(boost::shared_ptr<SessionData> session, unsigned remainingSec) {
m_server.SessionTimeoutWarning(session, remainingSec);
}
virtual void HandlePacket(boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet) {
m_server.DispatchPacket(session, packet);
}
2010-07-14 16:13:18 +00:00
virtual void SignalChatBotMessage(const string &msg) {
2009-08-17 09:45:06 +00:00
m_server.SendChatBotMsg(msg);
}
virtual void SignalChatBotMessage(unsigned gameId, const std::string &msg) {
m_server.SendChatBotMsg(gameId, msg);
}
virtual void SignalKickPlayer(unsigned playerId) {
2010-07-14 16:13:18 +00:00
m_server.RemovePlayer(playerId, ERR_NET_PLAYER_KICKED);
}
virtual void SignalBanPlayer(unsigned playerId) {
2010-07-14 17:20:51 +00:00
string playerName(m_server.GetPlayerNameFromId(playerId));
if (!playerName.empty()) {
2010-07-14 17:55:49 +00:00
m_server.GetBanManager().BanPlayerName(playerName, 1);
2010-07-14 17:20:51 +00:00
m_server.RemovePlayer(playerId, ERR_NET_PLAYER_KICKED);
}
2010-07-14 16:13:18 +00:00
}
virtual void SignalMutePlayer(unsigned playerId) {
m_server.MutePlayerInGame(playerId);
}
virtual void ConnectSuccess() {
LOG_MSG("Successfully connected to database.");
2009-09-19 20:28:30 +00:00
}
virtual void ConnectFailed(const string &error) {
LOG_ERROR("DB connect error: " << error);
}
virtual void QueryError(const std::string &error) {
LOG_ERROR("DB query error: " << error);
2009-09-19 20:28:30 +00:00
}
virtual void PlayerLoginSuccess(unsigned requestId, const DBPlayerData &dbPlayerData) {
m_server.UserValid(requestId, dbPlayerData);
}
virtual void PlayerLoginFailed(unsigned requestId) {
2009-10-11 12:07:56 +00:00
m_server.UserInvalid(requestId);
}
virtual void PlayerLoginBlocked(unsigned requestId) {
m_server.UserBlocked(requestId);
}
virtual void AvatarIsBlacklisted(unsigned requestId) {
m_server.AvatarBlacklisted(requestId);
}
virtual void AvatarIsOK(unsigned requestId) {
m_server.AvatarOK(requestId);
}
virtual void CreateGameSuccess(unsigned /*requestId*/) {
// Nothing to do.
2009-09-19 20:28:30 +00:00
}
virtual void CreateGameFailed(unsigned requestId) {
// TODO maybe handle request id.
LOG_ERROR("DB create game failed for request " << requestId);
2009-09-19 20:28:30 +00:00
}
virtual void ReportAvatarSuccess(unsigned requestId, unsigned replyId) {
m_server.SendReportAvatarResult(requestId, replyId, true);
}
virtual void ReportAvatarFailed(unsigned requestId, unsigned replyId) {
m_server.SendReportAvatarResult(requestId, replyId, false);
}
virtual void ReportGameSuccess(unsigned requestId, unsigned replyId) {
2012-04-09 10:22:24 +00:00
m_server.SendReportGameResult(requestId, replyId, true);
}
virtual void ReportGameFailed(unsigned requestId, unsigned replyId) {
2012-04-09 10:22:24 +00:00
m_server.SendReportGameResult(requestId, replyId, false);
}
private:
ServerLobbyThread &m_server;
};
ServerLobbyThread::ServerLobbyThread(GuiInterface &gui, ServerMode mode, ServerIrcBotCallback &ircBotCb, ConfigFile &serverConfig,
AvatarManager &avatarManager, boost::shared_ptr<boost::asio::io_service> ioService)
: m_ioService(ioService), m_authContext(NULL), m_gui(gui), m_ircBotCb(ircBotCb), m_avatarManager(avatarManager),
m_mode(mode), m_serverConfig(serverConfig), m_curGameId(0), m_curUniquePlayerId(0), m_curSessionId(INVALID_SESSION + 1),
2011-12-11 17:12:26 +00:00
m_statDataChanged(false), m_removeGameTimer(*ioService),
m_saveStatisticsTimer(*ioService), m_loginLockTimer(*ioService),
m_startTime(boost::posix_time::second_clock::local_time())
{
2009-09-19 20:28:30 +00:00
m_internalServerCallback.reset(new InternalServerCallback(*this));
m_sender.reset(new SenderHelper(m_ioService));
m_banManager.reset(new ServerBanManager(m_ioService));
2009-09-19 20:28:30 +00:00
m_chatCleanerManager.reset(new ChatCleanerManager(*m_internalServerCallback, m_ioService));
DBFactory dbFactory;
m_database = dbFactory.CreateServerDBObject(*m_internalServerCallback, m_ioService);
}
ServerLobbyThread::~ServerLobbyThread()
{
}
void
ServerLobbyThread::Init(const string &logDir)
{
// Read previous server statistics.
if (!logDir.empty()) {
boost::filesystem::path logPath(logDir);
if (!logDir.empty()) {
logPath /= SERVER_STATISTICS_FILE_NAME;
m_statisticsFileName = logPath.directory_string();
ReadStatisticsFile();
}
}
m_database->Init(
m_serverConfig.readConfigString("DBServerAddress"),
m_serverConfig.readConfigString("DBServerUser"),
m_serverConfig.readConfigString("DBServerPassword"),
m_serverConfig.readConfigString("DBServerDatabaseName"),
m_serverConfig.readConfigString("DBServerEncryptionKey"));
2010-09-05 00:15:25 +00:00
GetBanManager().InitGameNameBadWordList(m_serverConfig.readConfigStringList("GameNameBadWordList"));
}
void
ServerLobbyThread::SignalTermination()
{
Thread::SignalTermination();
m_ioService->stop();
}
void
ServerLobbyThread::AddConnection(boost::shared_ptr<tcp::socket> sock)
{
2009-05-10 14:57:51 +00:00
// Create a new session.
boost::shared_ptr<SessionData> sessionData(new SessionData(sock, m_curSessionId++, *m_internalServerCallback, GetIOService()));
2009-05-10 14:57:51 +00:00
m_sessionManager.AddSession(sessionData);
LOG_VERBOSE("Accepted connection - session #" << sessionData->GetId() << ".");
sessionData->StartTimerInitTimeout(SERVER_INIT_SESSION_TIMEOUT_SEC);
sessionData->StartTimerGlobalTimeout(SERVER_SESSION_FORCED_TIMEOUT_SEC);
sessionData->StartTimerActivityTimeout(SERVER_SESSION_ACTIVITY_TIMEOUT_SEC, SERVER_TIMEOUT_WARNING_REMAINING_SEC);
unsigned numLobbySessions = m_sessionManager.GetRawSessionCount();
unsigned numGameSessions = m_gameSessionManager.GetRawSessionCount();
if (numLobbySessions <= SERVER_MAX_NUM_LOBBY_SESSIONS
&& numLobbySessions + numGameSessions <= SERVER_MAX_NUM_TOTAL_SESSIONS) {
2012-09-02 12:35:41 +02:00
bool hasClientIp = false;
2009-05-10 14:57:51 +00:00
boost::system::error_code errCode;
tcp::endpoint clientEndpoint = sock->remote_endpoint(errCode);
if (!errCode) {
2009-05-10 14:57:51 +00:00
string ipAddress = clientEndpoint.address().to_string(errCode);
if (!errCode && !ipAddress.empty()) {
2009-05-10 14:57:51 +00:00
sessionData->SetClientAddr(ipAddress);
hasClientIp = true;
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AnnounceMessage);
AnnounceMessage *netAnnounce = packet->GetMsg()->mutable_announcemessage();
netAnnounce->mutable_protocolversion()->set_major(NET_VERSION_MAJOR);
netAnnounce->mutable_protocolversion()->set_minor(NET_VERSION_MINOR);
netAnnounce->mutable_latestgameversion()->set_major(POKERTH_VERSION_MAJOR);
netAnnounce->mutable_latestgameversion()->set_minor(POKERTH_VERSION_MINOR);
netAnnounce->set_latestbetarevision(POKERTH_BETA_REVISION);
switch (GetServerMode()) {
case SERVER_MODE_LAN:
netAnnounce->set_servertype(AnnounceMessage::serverTypeLAN);
break;
case SERVER_MODE_INTERNET_NOAUTH:
netAnnounce->set_servertype(AnnounceMessage::serverTypeInternetNoAuth);
break;
case SERVER_MODE_INTERNET_AUTH:
netAnnounce->set_servertype(AnnounceMessage::serverTypeInternetAuth);
break;
}
{
boost::mutex::scoped_lock lock(m_statMutex);
netAnnounce->set_numplayersonserver(m_statData.numberOfPlayersOnServer);
}
GetSender().Send(sessionData, packet);
sessionData->GetReceiveBuffer().StartAsyncRead(sessionData);
2009-05-10 14:57:51 +00:00
}
}
if (!hasClientIp) {
2009-05-10 14:57:51 +00:00
// We do not accept sessions if we cannot
// retrieve the client address.
SessionError(sessionData, ERR_NET_INVALID_SESSION);
2009-05-10 14:57:51 +00:00
}
} else {
2009-05-10 14:57:51 +00:00
// Server is full.
// Gracefully close this session.
SessionError(sessionData, ERR_NET_SERVER_FULL);
2009-05-10 14:57:51 +00:00
}
}
void
ServerLobbyThread::ReAddSession(boost::shared_ptr<SessionData> session, int reason, unsigned gameId)
{
if (session && session->GetPlayerData()) {
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_RemovedFromGameMessage);
RemovedFromGameMessage *removed = packet->GetMsg()->mutable_removedfromgamemessage();
removed->set_gameid(gameId);
switch (reason) {
case NTF_NET_REMOVED_GAME_FULL :
removed->set_removedfromgamereason(RemovedFromGameMessage::gameIsFull);
break;
case NTF_NET_REMOVED_ALREADY_RUNNING :
removed->set_removedfromgamereason(RemovedFromGameMessage::gameIsRunning);
break;
case NTF_NET_REMOVED_KICKED :
removed->set_removedfromgamereason(RemovedFromGameMessage::kickedFromGame);
break;
case NTF_NET_REMOVED_TIMEOUT :
removed->set_removedfromgamereason(RemovedFromGameMessage::gameTimeout);
break;
case NTF_NET_REMOVED_START_FAILED :
removed->set_removedfromgamereason(RemovedFromGameMessage::removedStartFailed);
break;
default :
removed->set_removedfromgamereason(RemovedFromGameMessage::removedOnRequest);
break;
}
GetSender().Send(session, packet);
HandleReAddedSession(session);
}
}
void
ServerLobbyThread::MoveSessionToGame(boost::shared_ptr<ServerGame> game, boost::shared_ptr<SessionData> session, bool autoLeave)
{
// Remove session from the lobby.
m_sessionManager.RemoveSession(session->GetId());
// Session is now in game state.
session->SetState(SessionData::Game);
// Store it in the list of game sessions.
m_gameSessionManager.AddSession(session);
// Set the game id of the session.
session->SetGame(game);
// Add session to the game.
game->AddSession(session);
// Optionally enable auto leave after game finish.
if (autoLeave)
game->SetPlayerAutoLeaveOnFinish(session->GetPlayerData()->GetUniqueId());
}
void
ServerLobbyThread::CloseSession(boost::shared_ptr<SessionData> session)
{
if (session && session->GetState() != SessionData::Closed) { // Make this call reentrant.
LOG_VERBOSE("Closing session #" << session->GetId() << ".");
session->SetState(SessionData::Closed);
2008-03-19 23:39:59 +00:00
boost::shared_ptr<ServerGame> tmpGame = session->GetGame();
if (tmpGame) {
tmpGame->RemoveSession(session, NTF_NET_INTERNAL);
}
m_sessionManager.RemoveSession(session->GetId());
m_gameSessionManager.RemoveSession(session->GetId());
if (session->GetPlayerData())
NotifyPlayerLeftLobby(session->GetPlayerData()->GetUniqueId());
// Update stats (if needed).
UpdateStatisticsNumberOfPlayers();
session->SetGame(boost::shared_ptr<ServerGame>());
// Ignore error when shutting down the socket.
boost::system::error_code ec;
session->GetAsioSocket()->shutdown(boost::asio::ip::tcp::socket::shutdown_receive, ec);
// Close this session after send.
GetSender().SetCloseAfterSend(session);
// Cancel all timers of the session.
session->CancelTimers();
}
}
void
ServerLobbyThread::ResubscribeLobbyMsg(boost::shared_ptr<SessionData> session)
{
InternalResubscribeMsg(session);
}
void
ServerLobbyThread::NotifyPlayerJoinedLobby(unsigned playerId)
{
boost::shared_ptr<NetPacket> notify = CreateNetPacketPlayerListNew(playerId);
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), notify, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), notify, SessionData::Game);
}
void
ServerLobbyThread::NotifyPlayerLeftLobby(unsigned playerId)
{
boost::shared_ptr<NetPacket> notify = CreateNetPacketPlayerListLeft(playerId);
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), notify, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), notify, SessionData::Game);
}
void
ServerLobbyThread::NotifyPlayerJoinedGame(unsigned gameId, unsigned playerId)
{
// Send notification to players in lobby.
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListPlayerJoinedMessage);
GameListPlayerJoinedMessage *netListMsg = packet->GetMsg()->mutable_gamelistplayerjoinedmessage();
netListMsg->set_gameid(gameId);
netListMsg->set_playerid(playerId);
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game);
}
void
ServerLobbyThread::NotifyPlayerLeftGame(unsigned gameId, unsigned playerId)
{
// Send notification to players in lobby.
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListPlayerLeftMessage);
GameListPlayerLeftMessage *netListMsg = packet->GetMsg()->mutable_gamelistplayerleftmessage();
netListMsg->set_gameid(gameId);
netListMsg->set_playerid(playerId);
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game);
}
void
ServerLobbyThread::NotifyGameAdminChanged(unsigned gameId, unsigned newAdminPlayerId)
{
// Send notification to players in lobby.
// Send notification to players in lobby.
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListAdminChangedMessage);
GameListAdminChangedMessage *netListMsg = packet->GetMsg()->mutable_gamelistadminchangedmessage();
netListMsg->set_gameid(gameId);
netListMsg->set_newadminplayerid(newAdminPlayerId);
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game);
}
void
ServerLobbyThread::NotifyStartingGame(unsigned gameId)
{
boost::shared_ptr<NetPacket> packet = CreateNetPacketGameListUpdate(gameId, GAME_MODE_STARTED);
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game);
}
void
ServerLobbyThread::NotifyReopeningGame(unsigned gameId)
{
boost::shared_ptr<NetPacket> packet = CreateNetPacketGameListUpdate(gameId, GAME_MODE_CREATED);
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game);
}
void
ServerLobbyThread::HandleGameRetrievePlayerInfo(boost::shared_ptr<SessionData> session, const PlayerInfoRequestMessage &playerInfoRequest)
{
// Someone within a game requested player info.
HandleNetPacketRetrievePlayerInfo(session, playerInfoRequest);
}
void
ServerLobbyThread::HandleGameRetrieveAvatar(boost::shared_ptr<SessionData> session, const AvatarRequestMessage &retrieveAvatar)
{
// Someone within a game requested an avatar.
HandleNetPacketRetrieveAvatar(session, retrieveAvatar);
}
void
ServerLobbyThread::HandleGameReportGame(boost::shared_ptr<SessionData> session, const ReportGameMessage &reportGame)
{
// Someone within a game reportet a game name.
HandleNetPacketReportGame(session, reportGame);
}
void
ServerLobbyThread::HandleChatRequest(boost::shared_ptr<SessionData> session, const ChatRequestMessage &chatRequest)
{
// Someone within a game sent a lobby message.
HandleNetPacketChatRequest(session, chatRequest);
}
bool
ServerLobbyThread::KickPlayerByName(const std::string &playerName)
{
bool retVal = false;
boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByPlayerName(playerName);
if (!session)
session = m_gameSessionManager.GetSessionByPlayerName(playerName);
if (session && session->GetPlayerData()) {
RemovePlayer(session->GetPlayerData()->GetUniqueId(), ERR_NET_PLAYER_KICKED);
retVal = true;
}
return retVal;
}
bool
ServerLobbyThread::RemoveGameByPlayerName(const std::string &playerName)
{
bool retVal = false;
boost::shared_ptr<SessionData> session = m_gameSessionManager.GetSessionByPlayerName(playerName);
if (session) {
boost::shared_ptr<ServerGame> game = session->GetGame();
if (game) {
m_ioService->post(boost::bind(&ServerLobbyThread::InternalRemoveGame, shared_from_this(), game));
retVal = true;
}
}
return retVal;
}
string
ServerLobbyThread::GetPlayerIPAddress(const std::string &playerName) const
{
string ipAddress;
boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByPlayerName(playerName);
if (!session)
session = m_gameSessionManager.GetSessionByPlayerName(playerName);
if (session)
ipAddress = session->GetClientAddr();
return ipAddress;
}
2010-07-14 17:20:51 +00:00
std::string
ServerLobbyThread::GetPlayerNameFromId(unsigned playerId) const
{
string name;
boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByUniquePlayerId(playerId);
if (!session)
2010-07-14 17:20:51 +00:00
session = m_gameSessionManager.GetSessionByUniquePlayerId(playerId);
if (session && session->GetPlayerData())
name = session->GetPlayerData()->GetName();
2010-07-14 17:20:51 +00:00
return name;
}
void
ServerLobbyThread::RemovePlayer(unsigned playerId, unsigned errorCode)
{
2011-12-11 17:12:26 +00:00
m_ioService->post(boost::bind(&ServerLobbyThread::InternalRemovePlayer, shared_from_this(), playerId, errorCode));
}
void
ServerLobbyThread::MutePlayerInGame(unsigned playerId)
{
m_ioService->post(boost::bind(&ServerLobbyThread::InternalMutePlayerInGame, shared_from_this(), playerId));
}
void
ServerLobbyThread::SendGlobalChat(const string &message)
{
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
netChat->set_chattype(ChatMessage::chatTypeBroadcast);
netChat->set_chattext(message);
m_sessionManager.SendToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendToAllSessions(GetSender(), packet, SessionData::Game);
}
void
ServerLobbyThread::SendGlobalMsgBox(const string &message)
{
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_DialogMessage);
DialogMessage *netDialog = packet->GetMsg()->mutable_dialogmessage();
netDialog->set_notificationtext(message);
2009-08-17 09:45:06 +00:00
m_sessionManager.SendToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendToAllSessions(GetSender(), packet, SessionData::Game);
}
2009-08-17 09:45:06 +00:00
void
ServerLobbyThread::SendChatBotMsg(const std::string &message)
{
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
netChat->set_chattype(ChatMessage::chatTypeBot);
netChat->set_chattext(message);
2009-08-17 09:45:06 +00:00
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game);
GetIrcBotCallback().SignalLobbyMessage(
0,
"(chat bot)",
message);
2009-08-17 09:45:06 +00:00
}
void
ServerLobbyThread::SendChatBotMsg(unsigned gameId, const std::string &message)
{
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
netChat->set_chattype(ChatMessage::chatTypeBot);
netChat->set_gameid(gameId);
netChat->set_chattext(message);
GameMap::const_iterator pos = m_gameMap.find(gameId);
if (pos != m_gameMap.end()) {
pos->second->SendToAllPlayers(packet, SessionData::Game);
}
}
void
ServerLobbyThread::ReconnectChatBot()
{
m_chatCleanerManager->ReInit();
}
void
ServerLobbyThread::AddComputerPlayer(boost::shared_ptr<PlayerData> player)
{
boost::mutex::scoped_lock lock(m_computerPlayersMutex);
m_computerPlayers.insert(PlayerDataMap::value_type(player->GetUniqueId(), player));
}
void
ServerLobbyThread::RemoveComputerPlayer(boost::shared_ptr<PlayerData> player)
{
boost::mutex::scoped_lock lock(m_computerPlayersMutex);
m_computerPlayers.erase(player->GetUniqueId());
}
2010-03-30 13:02:43 +00:00
bool
ServerLobbyThread::SendToLobbyPlayer(unsigned playerId, boost::shared_ptr<NetPacket> packet)
{
2010-03-30 13:02:43 +00:00
bool retVal = false;
boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(playerId);
if (tmpSession) {
GetSender().Send(tmpSession, packet);
2010-03-30 13:02:43 +00:00
retVal = true;
}
return retVal;
}
AvatarManager &
ServerLobbyThread::GetAvatarManager()
{
return m_avatarManager;
}
ChatCleanerManager &
ServerLobbyThread::GetChatCleaner()
{
assert(m_chatCleanerManager);
return *m_chatCleanerManager;
}
ServerStats
ServerLobbyThread::GetStats() const
{
boost::mutex::scoped_lock lock(m_statMutex);
return m_statData;
}
2007-11-28 23:31:34 +00:00
boost::posix_time::ptime
ServerLobbyThread::GetStartTime() const
{
return m_startTime;
}
ServerMode
ServerLobbyThread::GetServerMode() const
{
return m_mode;
}
SenderHelper &
ServerLobbyThread::GetSender()
{
assert(m_sender);
return *m_sender;
}
boost::asio::io_service &
ServerLobbyThread::GetIOService()
{
assert(m_ioService);
return *m_ioService;
}
boost::shared_ptr<ServerDBInterface>
ServerLobbyThread::GetDatabase()
{
return m_database;
}
ServerBanManager &
ServerLobbyThread::GetBanManager()
{
assert(m_banManager);
return *m_banManager;
}
u_int32_t
ServerLobbyThread::GetNextUniquePlayerId()
{
boost::mutex::scoped_lock lock(m_curUniquePlayerIdMutex);
m_curUniquePlayerId++;
if (m_curUniquePlayerId == 0) // 0 is an invalid id.
m_curUniquePlayerId++;
return m_curUniquePlayerId;
}
u_int32_t
ServerLobbyThread::GetNextGameId()
{
m_curGameId++;
if (m_curGameId == 0) // 0 is an invalid id.
m_curGameId++;
return m_curGameId;
}
void
ServerLobbyThread::Main()
{
try {
InitAuthContext();
InitChatCleaner();
// Start database engine.
m_database->Start();
// Register all timers.
RegisterTimers();
2009-06-17 21:22:01 +00:00
boost::asio::io_service::work ioWork(*m_ioService);
m_ioService->run(); // Will only be aborted asynchronously.
2009-06-04 22:13:52 +00:00
} catch (const PokerTHException &e) {
GetCallback().SignalNetServerError(e.GetErrorId(), e.GetOsErrorCode());
2009-07-12 21:12:55 +00:00
LOG_ERROR("Lobby exception: " << e.what());
}
// Clear all sessions and games.
2009-06-17 21:22:01 +00:00
m_sessionManager.Clear();
m_gameSessionManager.Clear();
BOOST_FOREACH(const GameMap::value_type& tmpGame, m_gameMap) {
tmpGame.second->Exit();
}
m_gameMap.clear();
// Cancel pending timer callbacks.
CancelTimers();
// Stop database engine.
m_database->Stop();
ClearAuthContext();
2009-05-10 14:57:51 +00:00
}
2009-05-10 14:57:51 +00:00
void
ServerLobbyThread::RegisterTimers()
{
// Remove closed games.
m_removeGameTimer.expires_from_now(
boost::posix_time::milliseconds(SERVER_REMOVE_GAME_INTERVAL_MSEC));
m_removeGameTimer.async_wait(
boost::bind(
&ServerLobbyThread::TimerRemoveGame, shared_from_this(), boost::asio::placeholders::error));
2009-05-10 14:57:51 +00:00
// Update the statistics file.
m_saveStatisticsTimer.expires_from_now(
boost::posix_time::seconds(SERVER_SAVE_STATISTICS_INTERVAL_SEC));
m_saveStatisticsTimer.async_wait(
boost::bind(
&ServerLobbyThread::TimerSaveStatisticsFile, shared_from_this(), boost::asio::placeholders::error));
// Update the avatar upload locks.
m_loginLockTimer.expires_from_now(
boost::posix_time::milliseconds(SERVER_UPDATE_LOGIN_LOCK_INTERVAL_MSEC));
m_loginLockTimer.async_wait(
boost::bind(
&ServerLobbyThread::TimerUpdateClientLoginLock, shared_from_this(), boost::asio::placeholders::error));
}
void
ServerLobbyThread::CancelTimers()
{
m_removeGameTimer.cancel();
m_saveStatisticsTimer.cancel();
m_loginLockTimer.cancel();
}
void
ServerLobbyThread::InitAuthContext()
{
2009-10-13 21:03:32 +00:00
int res = gsasl_init(&m_authContext);
if (res != GSASL_OK)
throw ServerException(__FILE__, __LINE__, ERR_NET_GSASL_INIT_FAILED, 0);
if (!gsasl_server_support_p(m_authContext, "SCRAM-SHA-1")) {
2009-10-13 21:03:32 +00:00
gsasl_done(m_authContext);
throw ServerException(__FILE__, __LINE__, ERR_NET_GSASL_NO_SCRAM, 0);
}
}
void
ServerLobbyThread::ClearAuthContext()
{
2009-10-11 12:07:56 +00:00
gsasl_done(m_authContext);
m_authContext = NULL;
}
2009-08-14 22:05:01 +00:00
void
ServerLobbyThread::InitChatCleaner()
{
if (m_serverConfig.readConfigInt("UseChatCleaner") != 0) {
2009-08-14 22:05:01 +00:00
m_chatCleanerManager->Init(
m_serverConfig.readConfigString("ChatCleanerHostAddress"),
m_serverConfig.readConfigInt("ChatCleanerPort"),
m_serverConfig.readConfigInt("ChatCleanerUseIpv6") != 0,
m_serverConfig.readConfigString("ChatCleanerClientAuth"),
m_serverConfig.readConfigString("ChatCleanerServerAuth"));
2009-08-14 22:05:01 +00:00
}
}
void
ServerLobbyThread::DispatchPacket(boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
{
if (session) {
// Retrieve current game, if applicable.
boost::shared_ptr<ServerGame> game = session->GetGame();
if (game) {
// We need to catch game-specific exceptions, so that they do not affect the server.
try {
game->HandlePacket(session, packet);
} catch (const PokerTHException &e) {
LOG_ERROR("Game " << game->GetId() << " - Read handler exception: " << e.what());
game->RemoveAllSessions();
}
} else
HandlePacket(session, packet);
}
}
void
ServerLobbyThread::HandlePacket(boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
{
if (session && packet) {
if (packet->IsClientActivity())
session->ResetActivityTimer();
if (session->GetState() == SessionData::Init) {
if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_InitMessage) {
HandleNetPacketInit(session, packet->GetMsg()->initmessage());
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AuthClientResponseMessage) {
HandleNetPacketAuthClientResponse(session, packet->GetMsg()->authclientresponsemessage());
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarHeaderMessage) {
HandleNetPacketAvatarHeader(session, packet->GetMsg()->avatarheadermessage());
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_UnknownAvatarMessage) {
HandleNetPacketUnknownAvatar(session, packet->GetMsg()->unknownavatarmessage());
} else {
SessionError(session, ERR_SOCK_INVALID_STATE);
}
} else if (session->GetState() == SessionData::ReceivingAvatar) {
if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarDataMessage) {
HandleNetPacketAvatarFile(session, packet->GetMsg()->avatardatamessage());
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarEndMessage) {
HandleNetPacketAvatarEnd(session, packet->GetMsg()->avatarendmessage());
} else {
SessionError(session, ERR_SOCK_INVALID_STATE);
}
} else {
if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_PlayerInfoRequestMessage)
HandleNetPacketRetrievePlayerInfo(session, packet->GetMsg()->playerinforequestmessage());
else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarRequestMessage)
HandleNetPacketRetrieveAvatar(session, packet->GetMsg()->avatarrequestmessage());
else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ResetTimeoutMessage)
2012-01-28 12:51:42 +00:00
{}
else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_SubscriptionRequestMessage) {
const SubscriptionRequestMessage &subscriptionRequest = packet->GetMsg()->subscriptionrequestmessage();
if (subscriptionRequest.subscriptionaction() == SubscriptionRequestMessage::resubscribeGameList)
InternalResubscribeMsg(session);
else
session->ResetWantsLobbyMsg();
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_JoinNewGameMessage) {
HandleNetPacketCreateGame(session, packet->GetMsg()->joinnewgamemessage());
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_JoinExistingGameMessage) {
HandleNetPacketJoinGame(session, packet->GetMsg()->joinexistinggamemessage());
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_RejoinExistingGameMessage) {
2012-12-21 01:01:01 +01:00
HandleNetPacketRejoinGame(session, packet->GetMsg()->rejoinexistinggamemessage());
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ChatRequestMessage) {
HandleNetPacketChatRequest(session, packet->GetMsg()->chatrequestmessage());
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_RejectGameInvitationMessage) {
HandleNetPacketRejectGameInvitation(session, packet->GetMsg()->rejectgameinvitationmessage());
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ReportGameMessage) {
HandleNetPacketReportGame(session, packet->GetMsg()->reportgamemessage());
} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_LeaveGameRequestMessage) {
// Ignore "leave game" in this state.
} else {
SessionError(session, ERR_SOCK_INVALID_STATE);
}
}
}
}
void
ServerLobbyThread::HandleNetPacketInit(boost::shared_ptr<SessionData> session, const InitMessage &initMessage)
{
LOG_VERBOSE("Received init for session #" << session->GetId() << ".");
2008-03-20 00:06:19 +00:00
// Before any other processing, perform some denial of service and
// brute force attack prevention by checking whether the user recently sent an
// Init packet.
if (m_serverConfig.readConfigInt("ServerBruteForceProtection") != 0) {
bool recentlySentInit = false;
{
boost::mutex::scoped_lock lock(m_timerClientAddressMapMutex);
if (m_timerClientAddressMap.find(session->GetClientAddr()) != m_timerClientAddressMap.end())
recentlySentInit = true;
else
m_timerClientAddressMap[session->GetClientAddr()] = boost::timers::portable::microsec_timer();
}
if (recentlySentInit) {
SessionError(session, ERR_NET_INIT_BLOCKED);
return;
}
}
// Check the protocol version.
if (initMessage.requestedversion().major() != NET_VERSION_MAJOR
|| session->GetPlayerData()) { // Has this session already sent an init?
SessionError(session, ERR_NET_VERSION_NOT_SUPPORTED);
return;
}
#ifndef POKERTH_OFFICIAL_SERVER
// Check (clear text) server password (skip for official server, they are open to everyone).
string serverPassword;
if (initMessage.has_authserverpassword()) {
serverPassword = initMessage.authserverpassword();
}
if (serverPassword != m_serverConfig.readConfigString("ServerPassword")) {
SessionError(session, ERR_NET_INVALID_PASSWORD);
return;
}
#endif
string playerName;
MD5Buf avatarMD5;
bool noAuth = false;
2009-11-15 08:41:29 +00:00
bool validGuest = false;
if (initMessage.login() == InitMessage::guestLogin) {
playerName = initMessage.nickname();
2009-11-15 08:41:29 +00:00
// Verify guest player name.
if (playerName.length() > sizeof(SERVER_GUEST_PLAYER_NAME - 1)
&& playerName.substr(0, sizeof(SERVER_GUEST_PLAYER_NAME) - 1) == SERVER_GUEST_PLAYER_NAME) {
2009-11-15 08:41:29 +00:00
string guestId(playerName.substr(sizeof(SERVER_GUEST_PLAYER_NAME)));
if ((size_t)count_if(guestId.begin(), guestId.end(), ::isdigit) == guestId.size()) {
2009-11-15 08:41:29 +00:00
validGuest = true;
noAuth = true;
}
}
if (!validGuest) {
2009-11-15 08:41:29 +00:00
SessionError(session, ERR_NET_INVALID_PLAYER_NAME);
2009-11-15 09:02:00 +00:00
return;
}
}
#ifdef POKERTH_OFFICIAL_SERVER
else if (initMessage.login() == InitMessage::authenticatedLogin) {
string inAuthData(initMessage.clientuserdata());
if (initMessage.has_avatarhash()) {
memcpy(avatarMD5.GetData(), initMessage.avatarhash().data(), MD5_DATA_SIZE);
}
session->CreateServerAuthSession(m_authContext);
if (session->AuthStep(1, inAuthData))
playerName = session->AuthGetUser();
}
#else
else if (initMessage.login() == InitMessage::unauthenticatedLogin) {
playerName = initMessage.nickname();
if (initMessage.has_avatarhash()) {
memcpy(avatarMD5.GetData(), initMessage.avatarhash().data(), MD5_DATA_SIZE);
}
noAuth = true;
}
#endif
else {
SessionError(session, ERR_NET_INVALID_PASSWORD);
2009-11-15 09:02:00 +00:00
return;
}
// Check whether the player name is correct.
// Partly, this is also done in netpacket.
// However, some disallowed names are checked only here.
if (playerName.empty()
|| playerName[0] == '#'
|| playerName[0] == ' '
|| playerName.substr(0, sizeof(SERVER_COMPUTER_PLAYER_NAME) - 1) == SERVER_COMPUTER_PLAYER_NAME) {
SessionError(session, ERR_NET_INVALID_PLAYER_NAME);
return;
}
// Check whether the player name is banned.
if (GetBanManager().IsPlayerBanned(playerName)) {
SessionError(session, ERR_NET_PLAYER_BANNED);
return;
}
2009-04-11 11:03:22 +00:00
// Check whether the peer IP address is banned.
if (GetBanManager().IsIPAddressBanned(session->GetClientAddr())) {
2009-04-11 11:03:22 +00:00
SessionError(session, ERR_NET_PLAYER_BANNED);
return;
}
// Create player data object.
boost::shared_ptr<PlayerData> tmpPlayerData(
new PlayerData(GetNextUniquePlayerId(), 0, PLAYER_TYPE_HUMAN, validGuest ? PLAYER_RIGHTS_GUEST : PLAYER_RIGHTS_NORMAL, false));
tmpPlayerData->SetName(playerName);
tmpPlayerData->SetAvatarMD5(avatarMD5);
if (initMessage.has_mylastsessionid()) {
tmpPlayerData->SetOldGuid(initMessage.mylastsessionid());
}
// Set player data for session.
m_sessionManager.SetSessionPlayerData(session->GetId(), tmpPlayerData);
session->SetPlayerData(tmpPlayerData);
if (noAuth)
2009-09-19 21:01:51 +00:00
InitAfterLogin(session);
else
AuthenticatePlayer(session);
}
void
ServerLobbyThread::HandleNetPacketAuthClientResponse(boost::shared_ptr<SessionData> session, const AuthClientResponseMessage &clientResponse)
{
if (session && session->GetPlayerData() && session->AuthGetCurStepNum() == 1) {
string authData = clientResponse.clientresponse();
if (session->AuthStep(2, authData)) {
string outVerification(session->AuthGetNextOutMsg());
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AuthServerVerificationMessage);
AuthServerVerificationMessage *verification = packet->GetMsg()->mutable_authserververificationmessage();
verification->set_serververification(outVerification);
GetSender().Send(session, packet);
// The last message is only for server verification.
// We are done now, the user has logged in.
CheckAvatarBlacklist(session);
} else
SessionError(session, ERR_NET_INVALID_PASSWORD);
}
}
void
ServerLobbyThread::HandleNetPacketAvatarHeader(boost::shared_ptr<SessionData> session, const AvatarHeaderMessage &avatarHeader)
{
if (session->GetPlayerData()) {
if (avatarHeader.avatarsize() >= MIN_AVATAR_FILE_SIZE && avatarHeader.avatarsize() <= MAX_AVATAR_FILE_SIZE) {
boost::shared_ptr<AvatarFile> tmpAvatarFile(new AvatarFile);
tmpAvatarFile->fileData.reserve(avatarHeader.avatarsize());
tmpAvatarFile->fileType = static_cast<AvatarFileType>(avatarHeader.avatartype());
tmpAvatarFile->reportedSize = avatarHeader.avatarsize();
// Ignore request id for now.
session->GetPlayerData()->SetNetAvatarFile(tmpAvatarFile);
// Session is now receiving an avatar.
session->SetState(SessionData::ReceivingAvatar);
} else
SessionError(session, ERR_NET_AVATAR_TOO_LARGE);
}
}
void
ServerLobbyThread::HandleNetPacketUnknownAvatar(boost::shared_ptr<SessionData> session, const UnknownAvatarMessage &/*unknownAvatar*/)
{
if (session->GetPlayerData()) {
// Free memory (just in case).
session->GetPlayerData()->SetNetAvatarFile(boost::shared_ptr<AvatarFile>());
session->GetPlayerData()->SetAvatarMD5(MD5Buf());
// Start session.
EstablishSession(session);
}
}
void
ServerLobbyThread::HandleNetPacketAvatarFile(boost::shared_ptr<SessionData> session, const AvatarDataMessage &avatarData)
{
if (session->GetPlayerData()) {
boost::shared_ptr<AvatarFile> tmpAvatar = session->GetPlayerData()->GetNetAvatarFile();
const string &avatarBlock = avatarData.avatarblock();
if (tmpAvatar && tmpAvatar->fileData.size() + avatarBlock.size() <= tmpAvatar->reportedSize) {
std::copy(&avatarBlock[0], &avatarBlock[avatarBlock.size()], back_inserter(tmpAvatar->fileData));
}
}
}
void
ServerLobbyThread::HandleNetPacketAvatarEnd(boost::shared_ptr<SessionData> session, const AvatarEndMessage &/*avatarEnd*/)
{
if (session->GetPlayerData()) {
boost::shared_ptr<AvatarFile> tmpAvatar = session->GetPlayerData()->GetNetAvatarFile();
MD5Buf avatarMD5 = session->GetPlayerData()->GetAvatarMD5();
if (!avatarMD5.IsZero() && tmpAvatar.get()) {
unsigned avatarSize = (unsigned)tmpAvatar->fileData.size();
if (avatarSize == tmpAvatar->reportedSize) {
if (!GetAvatarManager().StoreAvatarInCache(avatarMD5, tmpAvatar->fileType, &tmpAvatar->fileData[0], avatarSize, true)) {
session->GetPlayerData()->SetAvatarMD5(MD5Buf());
LOG_ERROR("Failed to store avatar in cache directory.");
}
// Free memory.
session->GetPlayerData()->SetNetAvatarFile(boost::shared_ptr<AvatarFile>());
// Set avatar file name.
string avatarFileName;
if (GetAvatarManager().GetAvatarFileName(avatarMD5, avatarFileName))
session->GetPlayerData()->SetAvatarFile(avatarFileName);
// Init finished - start session.
EstablishSession(session);
LOG_MSG("Client \"" << session->GetClientAddr() << "\" uploaded avatar \""
<< boost::filesystem::path(avatarFileName).file_string() << "\".");
} else
SessionError(session, ERR_NET_WRONG_AVATAR_SIZE);
}
}
}
void
ServerLobbyThread::HandleNetPacketRetrievePlayerInfo(boost::shared_ptr<SessionData> session, const PlayerInfoRequestMessage &playerInfoRequest)
{
BOOST_FOREACH(unsigned playerId, playerInfoRequest.playerid()) {
// Find player in lobby or in a game.
boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(playerId);
if (!tmpSession) {
tmpSession = m_gameSessionManager.GetSessionByUniquePlayerId(playerId);
}
boost::shared_ptr<PlayerData> tmpPlayer;
if (tmpSession) {
tmpPlayer = tmpSession->GetPlayerData();
}
if (!tmpPlayer) {
boost::mutex::scoped_lock lock(m_computerPlayersMutex);
PlayerDataMap::const_iterator pos = m_computerPlayers.find(playerId);
if (pos != m_computerPlayers.end())
tmpPlayer = pos->second;
}
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayerInfoReplyMessage);
PlayerInfoReplyMessage *netPlayerInfoReply = packet->GetMsg()->mutable_playerinforeplymessage();
netPlayerInfoReply->set_playerid(playerId);
if (tmpPlayer) {
// Send player info to client.
PlayerInfoReplyMessage::PlayerInfoData *data = netPlayerInfoReply->mutable_playerinfodata();
data->set_playername(tmpPlayer->GetName());
data->set_ishuman(tmpPlayer->GetType() == PLAYER_TYPE_HUMAN);
data->set_playerrights(static_cast<NetPlayerInfoRights>(tmpPlayer->GetRights()));
if (!tmpPlayer->GetCountry().empty()) {
data->set_countrycode(tmpPlayer->GetCountry());
}
if (!tmpPlayer->GetAvatarMD5().IsZero()) {
PlayerInfoReplyMessage::PlayerInfoData::AvatarData *avatarData = data->mutable_avatardata();
avatarData->set_avatartype(static_cast<NetAvatarType>(AvatarManager::GetAvatarFileType(tmpPlayer->GetAvatarFile())));
avatarData->set_avatarhash(tmpPlayer->GetAvatarMD5().GetData(), MD5_DATA_SIZE);
}
} else {
// Unknown player id - do not set any data.
}
GetSender().Send(session, packet);
}
}
void
ServerLobbyThread::HandleNetPacketRetrieveAvatar(boost::shared_ptr<SessionData> session, const AvatarRequestMessage &retrieveAvatar)
{
bool avatarFound = false;
string tmpFile;
MD5Buf tmpMD5;
memcpy(tmpMD5.GetData(), retrieveAvatar.avatarhash().data(), MD5_DATA_SIZE);
if (GetAvatarManager().GetAvatarFileName(tmpMD5, tmpFile)) {
NetPacketList tmpPackets;
if (GetAvatarManager().AvatarFileToNetPackets(tmpFile, retrieveAvatar.requestid(), tmpPackets) == 0) {
avatarFound = true;
GetSender().Send(session, tmpPackets);
} else
LOG_ERROR("Failed to read avatar file for network transmission.");
}
if (!avatarFound) {
// Notify client we didn't find the avatar.
boost::shared_ptr<NetPacket> unknownAvatar(new NetPacket);
unknownAvatar->GetMsg()->set_messagetype(PokerTHMessage::Type_UnknownAvatarMessage);
UnknownAvatarMessage *netAvatarReply = unknownAvatar->GetMsg()->mutable_unknownavatarmessage();
netAvatarReply->set_requestid(retrieveAvatar.requestid());
GetSender().Send(session, unknownAvatar);
}
}
void
ServerLobbyThread::HandleNetPacketCreateGame(boost::shared_ptr<SessionData> session, const JoinNewGameMessage &newGame)
{
LOG_VERBOSE("Creating new game, initiated by session #" << session->GetId() << ".");
2008-03-19 23:39:59 +00:00
string password;
if (newGame.has_password())
password = newGame.password();
// Create a new game.
GameData tmpData;
NetPacket::GetGameData(newGame.gameinfo(), tmpData);
string gameName(newGame.gameinfo().gamename());
// Always trim the game name.
boost::trim(gameName);
boost::replace_all(gameName, "\n", " ");
boost::replace_all(gameName, "\r", " ");
boost::replace_all(gameName, "\t", " ");
boost::replace_all(gameName, "\v", " ");
boost::replace_all(gameName, "\f", " ");
unsigned gameId = GetNextGameId();
if (gameName.empty() || !isprint(gameName[0])) {
SendJoinGameFailed(session, gameId, NTF_NET_JOIN_GAME_BAD_NAME);
} else if (IsGameNameInUse(gameName)) {
SendJoinGameFailed(session, gameId, NTF_NET_JOIN_GAME_NAME_IN_USE);
} else if (GetBanManager().IsBadGameName(gameName)) {
SendJoinGameFailed(session, gameId, NTF_NET_JOIN_GAME_BAD_NAME);
} else if (session->GetPlayerData()->GetRights() == PLAYER_RIGHTS_GUEST
&& tmpData.gameType != GAME_TYPE_NORMAL) {
SendJoinGameFailed(session, gameId, NTF_NET_JOIN_GUEST_FORBIDDEN);
} else if (!ServerGame::CheckSettings(tmpData, password, GetServerMode())) {
SendJoinGameFailed(session, gameId, NTF_NET_JOIN_INVALID_SETTINGS);
} else {
boost::shared_ptr<ServerGame> game(
new ServerGame(
shared_from_this(),
gameId,
gameName,
password,
tmpData,
session->GetPlayerData()->GetUniqueId(),
session->GetPlayerData()->GetDBId(),
GetGui(),
m_serverConfig));
game->Init();
// Add game to list of games.
InternalAddGame(game);
MoveSessionToGame(game, session, newGame.autoleave());
}
}
void
ServerLobbyThread::HandleNetPacketJoinGame(boost::shared_ptr<SessionData> session, const JoinExistingGameMessage &joinGame)
{
string password;
if (joinGame.has_password())
password = joinGame.password();
// Join an existing game.
GameMap::iterator pos = m_gameMap.find(joinGame.gameid());
if (pos != m_gameMap.end()) {
boost::shared_ptr<ServerGame> game = pos->second;
const GameData &tmpData = game->GetGameData();
if (session->GetPlayerData()->GetRights() == PLAYER_RIGHTS_GUEST
&& tmpData.gameType != GAME_TYPE_NORMAL) {
SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_GUEST_FORBIDDEN);
} else if (tmpData.gameType == GAME_TYPE_INVITE_ONLY
&& !game->IsPlayerInvited(session->GetPlayerData()->GetUniqueId())) {
SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_NOT_INVITED);
} else if (!game->CheckPassword(password)) {
SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_INVALID_PASSWORD);
} else if (tmpData.gameType == GAME_TYPE_RANKING
&& session->GetClientAddr() != SERVER_ADDRESS_LOCALHOST_STR
&& session->GetClientAddr() != SERVER_ADDRESS_LOCALHOST_STR_V4V6
&& session->GetClientAddr() != SERVER_ADDRESS_LOCALHOST_STR_V4
&& game->IsClientAddressConnected(session->GetClientAddr())) {
SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_IP_BLOCKED);
} else {
MoveSessionToGame(game, session, joinGame.autoleave());
}
} else {
SendJoinGameFailed(session, joinGame.gameid(), NTF_NET_JOIN_GAME_INVALID);
}
}
void
ServerLobbyThread::HandleNetPacketRejoinGame(boost::shared_ptr<SessionData> session, const RejoinExistingGameMessage &rejoinGame)
{
// Rejoin a running game.
GameMap::iterator pos = m_gameMap.find(rejoinGame.gameid());
if (pos != m_gameMap.end()) {
boost::shared_ptr<ServerGame> game = pos->second;
MoveSessionToGame(game, session, rejoinGame.autoleave());
} else {
SendJoinGameFailed(session, rejoinGame.gameid(), NTF_NET_JOIN_GAME_INVALID);
}
}
void
ServerLobbyThread::HandleNetPacketChatRequest(boost::shared_ptr<SessionData> session, const ChatRequestMessage &chatRequest)
{
bool chatSent = false;
// Guests are not allowed to chat.
if (session->GetPlayerData() && session->GetPlayerData()->GetRights() != PLAYER_RIGHTS_GUEST) {
if (!chatRequest.has_targetgameid() && !chatRequest.has_targetplayerid()) {
string chatMsg(chatRequest.chattext());
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
netChat->set_chattype(ChatMessage::chatTypeLobby);
netChat->set_playerid(session->GetPlayerData()->GetUniqueId());
netChat->set_chattext(chatMsg);
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game);
// Send the message to the chat cleaner bot.
m_chatCleanerManager->HandleLobbyChatText(
session->GetPlayerData()->GetUniqueId(),
session->GetPlayerData()->GetName(),
chatMsg);
// Send the message to the irc bot.
GetIrcBotCallback().SignalLobbyMessage(
session->GetPlayerData()->GetUniqueId(),
session->GetPlayerData()->GetName(),
chatMsg);
chatSent = true;
} else if (!chatRequest.has_targetgameid() && chatRequest.has_targetplayerid()) {
boost::shared_ptr<SessionData> targetSession = m_sessionManager.GetSessionByUniquePlayerId(chatRequest.targetplayerid());
if (!targetSession)
targetSession = m_gameSessionManager.GetSessionByUniquePlayerId(chatRequest.targetplayerid());
if (targetSession && targetSession->GetPlayerData()) {
// Only allow private messages to players which are not in running games.
boost::shared_ptr<ServerGame> tmpGame = targetSession->GetGame();
if (!tmpGame || !tmpGame->IsRunning()) {
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
netChat->set_chattype(ChatMessage::chatTypePrivate);
netChat->set_playerid(session->GetPlayerData()->GetUniqueId());
netChat->set_chattext(chatRequest.chattext());
GetSender().Send(targetSession, packet);
chatSent = true;
}
}
}
}
// Other chat types are not allowed in the lobby.
if (!chatSent) {
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatRejectMessage);
ChatRejectMessage *netReject = packet->GetMsg()->mutable_chatrejectmessage();
netReject->set_chattext(chatRequest.chattext());
GetSender().Send(session, packet);
}
}
2010-03-30 17:10:37 +00:00
void
ServerLobbyThread::HandleNetPacketRejectGameInvitation(boost::shared_ptr<SessionData> session, const RejectGameInvitationMessage &reject)
2010-03-30 17:10:37 +00:00
{
GameMap::iterator pos = m_gameMap.find(reject.gameid());
2010-03-30 17:10:37 +00:00
if (pos != m_gameMap.end() && session->GetPlayerData()) {
2010-03-30 17:10:37 +00:00
ServerGame &game = *pos->second;
unsigned tmpPlayerId = session->GetPlayerData()->GetUniqueId();
if (game.IsPlayerInvited(tmpPlayerId)) {
// If he actively rejects, he is no longer invited.
if (reject.myrejectreason() == RejectGameInvitationMessage::rejectReasonNo) {
game.RemovePlayerInvitation(tmpPlayerId);
}
// Send reject notification.
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_RejectInvNotifyMessage);
RejectInvNotifyMessage *netReject = packet->GetMsg()->mutable_rejectinvnotifymessage();
netReject->set_gameid(reject.gameid());
netReject->set_playerid(tmpPlayerId);
netReject->set_playerrejectreason(reject.myrejectreason());
2010-03-30 17:10:37 +00:00
game.SendToAllPlayers(packet, SessionData::Game);
}
}
}
void
ServerLobbyThread::HandleNetPacketReportGame(boost::shared_ptr<SessionData> session, const ReportGameMessage &report)
{
GameMap::iterator pos = m_gameMap.find(report.reportedgameid());
if (pos != m_gameMap.end() && session->GetPlayerData()) {
2012-04-09 10:22:24 +00:00
boost::shared_ptr<ServerGame> tmpGame(pos->second);
if (!tmpGame->IsNameReported()) {
// Temporarily note that this game was reported.
// This prevents spamming of the game report.
2012-04-09 10:22:24 +00:00
tmpGame->SetNameReported();
unsigned creatorDBId = tmpGame->GetCreatorDBId();
unsigned reporterDBId = session->GetPlayerData()->GetDBId();
GetDatabase()->AsyncReportGame(
session->GetPlayerData()->GetUniqueId(),
tmpGame->GetId(),
creatorDBId != 0 ? &creatorDBId : NULL,
tmpGame->GetId(),
tmpGame->GetName(),
reporterDBId != 0 ? &reporterDBId : NULL
);
} else {
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ReportGameAckMessage);
ReportGameAckMessage *netReportAck = packet->GetMsg()->mutable_reportgameackmessage();
netReportAck->set_reportedgameid(report.reportedgameid());
netReportAck->set_reportgameresult(ReportGameAckMessage::gameReportDuplicate);
GetSender().Send(session, packet);
}
} else {
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ReportGameAckMessage);
ReportGameAckMessage *netReportAck = packet->GetMsg()->mutable_reportgameackmessage();
netReportAck->set_reportedgameid(report.reportedgameid());
netReportAck->set_reportgameresult(ReportGameAckMessage::gameReportInvalid);
GetSender().Send(session, packet);
}
}
void
ServerLobbyThread::AuthChallenge(boost::shared_ptr<SessionData> session, const string &secret)
{
if (session && session->GetPlayerData() && session->AuthGetCurStepNum() == 1) {
session->AuthSetPassword(secret); // For this auth session.
string outChallenge(session->AuthGetNextOutMsg());
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AuthServerChallengeMessage);
AuthServerChallengeMessage *challenge = packet->GetMsg()->mutable_authserverchallengemessage();
challenge->set_serverchallenge(outChallenge);
GetSender().Send(session, packet);
}
}
void
ServerLobbyThread::CheckAvatarBlacklist(boost::shared_ptr<SessionData> session)
{
if (session && session->GetPlayerData()) {
const MD5Buf &avatarMD5 = session->GetPlayerData()->GetAvatarMD5();
if (!avatarMD5.IsZero())
m_database->AsyncCheckAvatarBlacklist(session->GetPlayerData()->GetUniqueId(), avatarMD5.ToString());
else
InitAfterLogin(session);
}
}
void
ServerLobbyThread::AvatarBlacklisted(unsigned playerId)
{
boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(playerId, true);
if (tmpSession && tmpSession->GetPlayerData()) {
tmpSession->GetPlayerData()->SetAvatarMD5(MD5Buf()); // Reset avatar if blacklisted.
InitAfterLogin(tmpSession);
}
}
void
ServerLobbyThread::AvatarOK(unsigned playerId)
{
boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(playerId, true);
InitAfterLogin(tmpSession);
}
2009-09-19 21:01:51 +00:00
void
ServerLobbyThread::InitAfterLogin(boost::shared_ptr<SessionData> session)
2009-09-19 21:01:51 +00:00
{
if (session && session->GetPlayerData()) {
const MD5Buf &avatarMD5 = session->GetPlayerData()->GetAvatarMD5();
2009-09-26 16:14:22 +00:00
string avatarFileName;
if (!avatarMD5.IsZero()
&& !GetAvatarManager().GetAvatarFileName(avatarMD5, avatarFileName)) {
RequestPlayerAvatar(session);
} else {
2009-09-26 16:14:22 +00:00
if (!avatarFileName.empty())
session->GetPlayerData()->SetAvatarFile(avatarFileName);
2009-09-26 16:14:22 +00:00
EstablishSession(session);
}
2009-09-19 21:01:51 +00:00
}
}
void
ServerLobbyThread::EstablishSession(boost::shared_ptr<SessionData> session)
{
if (!session->GetPlayerData())
throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_SESSION, 0);
2010-08-23 17:37:22 +00:00
unsigned rejoinPlayerId = 0;
u_int32_t rejoinGameId = GetRejoinGameIdForPlayer(session->GetPlayerData()->GetName(), session->GetPlayerData()->GetOldGuid(), rejoinPlayerId);
if (rejoinGameId != 0) {
// Offer rejoin, and disconnect current player with the same name.
InternalRemovePlayer(rejoinPlayerId, ERR_NET_PLAYER_NAME_IN_USE);
} else {
// Check whether this player is already connected.
unsigned previousPlayerId = GetPlayerId(session->GetPlayerData()->GetName());
if (previousPlayerId != 0 && previousPlayerId != session->GetPlayerData()->GetUniqueId()) {
2012-01-02 10:42:35 +00:00
#ifdef POKERTH_OFFICIAL_SERVER
// If so, and this is a login server, disconnect the already connected player.
InternalRemovePlayer(previousPlayerId, ERR_NET_PLAYER_NAME_IN_USE);
#else
2012-01-02 10:42:35 +00:00
// If this is a server without password protection, close this new session and return.
SessionError(session, ERR_NET_PLAYER_NAME_IN_USE);
return;
#endif
2012-01-02 10:42:35 +00:00
}
}
2010-08-23 17:37:22 +00:00
// Run postlogin for DB
string tmpAvatarHash;
string tmpAvatarType;
if (!session->GetPlayerData()->GetAvatarMD5().IsZero()) {
tmpAvatarHash = session->GetPlayerData()->GetAvatarMD5().ToString();
tmpAvatarType = AvatarManager::GetAvatarFileExtension(AvatarManager::GetAvatarFileType(session->GetPlayerData()->GetAvatarFile()));
2010-08-23 17:37:22 +00:00
if (!tmpAvatarType.empty())
tmpAvatarType.erase(0, 1); // Only store extension without the "."
}
m_database->PlayerPostLogin(session->GetPlayerData()->GetDBId(), tmpAvatarHash, tmpAvatarType);
2010-08-23 17:37:22 +00:00
// Generate a new GUID.
boost::uuids::uuid sessionGuid(m_sessionIdGenerator());
session->GetPlayerData()->SetGuid(string((char *)&sessionGuid, boost::uuids::uuid::static_size()));
// Send ACK to client.
boost::shared_ptr<NetPacket> ack(new NetPacket);
ack->GetMsg()->set_messagetype(PokerTHMessage::Type_InitAckMessage);
InitAckMessage *netInitAck = ack->GetMsg()->mutable_initackmessage();
netInitAck->set_yoursessionid(session->GetPlayerData()->GetGuid());
netInitAck->set_yourplayerid(session->GetPlayerData()->GetUniqueId());
if (rejoinGameId != 0) {
netInitAck->set_rejoingameid(rejoinGameId);
}
GetSender().Send(session, ack);
// Send the connected players list to the client.
SendPlayerList(session);
// Send the game list to the client.
SendGameList(session);
// Session is now established.
session->SetState(SessionData::Established);
{
boost::mutex::scoped_lock lock(m_statMutex);
++m_statData.totalPlayersEverLoggedIn;
m_statDataChanged = true;
}
// Notify all players.
NotifyPlayerJoinedLobby(session->GetPlayerData()->GetUniqueId());
UpdateStatisticsNumberOfPlayers();
}
2009-09-19 21:01:51 +00:00
void
ServerLobbyThread::AuthenticatePlayer(boost::shared_ptr<SessionData> session)
2009-09-19 21:01:51 +00:00
{
if(session->GetPlayerData()) {
m_database->AsyncPlayerLogin(session->GetPlayerData()->GetUniqueId(), session->GetPlayerData()->GetName());
2010-08-22 19:35:43 +00:00
}
}
void
ServerLobbyThread::UserValid(unsigned playerId, const DBPlayerData &dbPlayerData)
{
boost::shared_ptr<SessionData> tmpSession = m_sessionManager.GetSessionByUniquePlayerId(playerId, true);
if (tmpSession && tmpSession->GetPlayerData()) {
tmpSession->GetPlayerData()->SetDBId(dbPlayerData.id);
tmpSession->GetPlayerData()->SetCountry(dbPlayerData.country);
this->AuthChallenge(tmpSession, dbPlayerData.secret);
}
}
void
2009-10-11 12:07:56 +00:00
ServerLobbyThread::UserInvalid(unsigned playerId)
{
SessionError(m_sessionManager.GetSessionByUniquePlayerId(playerId, true), ERR_NET_INVALID_PASSWORD);
2009-09-19 21:01:51 +00:00
}
void
ServerLobbyThread::SendReportAvatarResult(unsigned byPlayerId, unsigned reportedPlayerId, bool success)
{
boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByUniquePlayerId(byPlayerId);
if (!session)
session = m_gameSessionManager.GetSessionByUniquePlayerId(byPlayerId);
if (session) {
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ReportAvatarAckMessage);
ReportAvatarAckMessage *netReportAck = packet->GetMsg()->mutable_reportavatarackmessage();
netReportAck->set_reportedplayerid(reportedPlayerId);
netReportAck->set_reportavatarresult(success ? ReportAvatarAckMessage::avatarReportAccepted : ReportAvatarAckMessage::avatarReportInvalid);
GetSender().Send(session, packet);
}
}
2012-04-09 10:22:24 +00:00
void
ServerLobbyThread::SendReportGameResult(unsigned byPlayerId, unsigned reportedGameId, bool success)
{
boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByUniquePlayerId(byPlayerId);
if (!session)
session = m_gameSessionManager.GetSessionByUniquePlayerId(byPlayerId);
if (session) {
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ReportGameAckMessage);
ReportGameAckMessage *netReportAck = packet->GetMsg()->mutable_reportgameackmessage();
netReportAck->set_reportedgameid(reportedGameId);
netReportAck->set_reportgameresult(success ? ReportGameAckMessage::gameReportAccepted : ReportGameAckMessage::gameReportInvalid);
2012-04-09 10:22:24 +00:00
GetSender().Send(session, packet);
}
}
void
ServerLobbyThread::UserBlocked(unsigned playerId)
{
2010-07-14 15:40:14 +00:00
SessionError(m_sessionManager.GetSessionByUniquePlayerId(playerId, true), ERR_NET_PLAYER_BLOCKED);
}
void
ServerLobbyThread::RequestPlayerAvatar(boost::shared_ptr<SessionData> session)
{
if (!session->GetPlayerData())
throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_SESSION, 0);
// Ask the client to send its avatar.
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AvatarRequestMessage);
AvatarRequestMessage *netAvatarRequest = packet->GetMsg()->mutable_avatarrequestmessage();
netAvatarRequest->set_requestid(session->GetPlayerData()->GetUniqueId());
netAvatarRequest->set_avatarhash(session->GetPlayerData()->GetAvatarMD5().GetData(), MD5_DATA_SIZE);
GetSender().Send(session, packet);
}
void
ServerLobbyThread::TimerRemoveGame(const boost::system::error_code &ec)
{
if (!ec) {
// Synchronously remove games which have been closed.
GameMap::iterator i = m_gameMap.begin();
GameMap::iterator end = m_gameMap.end();
while (i != end) {
GameMap::iterator next = i;
++next;
boost::shared_ptr<ServerGame> tmpGame = i->second;
if (!tmpGame->GetSessionManager().HasSessions())
InternalRemoveGame(tmpGame); // This will delete the game.
i = next;
}
2009-06-12 16:04:58 +00:00
// Restart timer
m_removeGameTimer.expires_from_now(
boost::posix_time::milliseconds(SERVER_REMOVE_GAME_INTERVAL_MSEC));
m_removeGameTimer.async_wait(
boost::bind(
&ServerLobbyThread::TimerRemoveGame, shared_from_this(), boost::asio::placeholders::error));
}
}
void
ServerLobbyThread::TimerUpdateClientLoginLock(const boost::system::error_code &ec)
{
if (!ec) {
boost::mutex::scoped_lock lock(m_timerClientAddressMapMutex);
TimerClientAddressMap::iterator i = m_timerClientAddressMap.begin();
TimerClientAddressMap::iterator end = m_timerClientAddressMap.end();
while (i != end) {
TimerClientAddressMap::iterator next = i;
++next;
2010-10-11 15:44:56 +00:00
if (i->second.elapsed().total_seconds() > (int)SERVER_INIT_LOGIN_CLIENT_LOCK_SEC)
m_timerClientAddressMap.erase(i);
i = next;
}
2009-06-12 16:04:58 +00:00
// Restart timer
m_loginLockTimer.expires_from_now(
boost::posix_time::milliseconds(SERVER_UPDATE_LOGIN_LOCK_INTERVAL_MSEC));
m_loginLockTimer.async_wait(
2009-06-12 16:04:58 +00:00
boost::bind(
&ServerLobbyThread::TimerUpdateClientLoginLock, shared_from_this(), boost::asio::placeholders::error));
}
}
bool
ServerLobbyThread::IsGameNameInUse(const std::string &gameName) const
{
bool found = false;
GameMap::const_iterator i = m_gameMap.begin();
GameMap::const_iterator end = m_gameMap.end();
while (i != end) {
if ((*i).second->GetName() == gameName) {
found = true;
break;
}
++i;
}
return found;
}
boost::shared_ptr<ServerGame>
ServerLobbyThread::InternalGetGameFromId(unsigned gameId)
{
boost::shared_ptr<ServerGame> game;
if (gameId) {
GameMap::iterator pos = m_gameMap.find(gameId);
if (pos != m_gameMap.end())
game = pos->second;
}
return game;
}
void
ServerLobbyThread::InternalAddGame(boost::shared_ptr<ServerGame> game)
{
// Add game to list.
m_gameMap.insert(GameMap::value_type(game->GetId(), game));
// Notify all players.
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), CreateNetPacketGameListNew(*game), SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), CreateNetPacketGameListNew(*game), SessionData::Game);
{
boost::mutex::scoped_lock lock(m_statMutex);
++m_statData.totalGamesEverCreated;
2008-04-01 21:44:05 +00:00
++m_statData.numberOfGamesOpen;
unsigned numGames = static_cast<unsigned>(m_gameMap.size());
if (numGames > m_statData.maxGamesOpen)
m_statData.maxGamesOpen = numGames;
m_statDataChanged = true;
}
}
void
ServerLobbyThread::InternalRemoveGame(boost::shared_ptr<ServerGame> game)
{
2008-04-01 21:44:05 +00:00
{
boost::mutex::scoped_lock lock(m_statMutex);
if (m_statData.numberOfGamesOpen) {
2008-04-01 21:44:05 +00:00
--m_statData.numberOfGamesOpen;
m_statDataChanged = true;
}
}
// Remove game from list.
m_gameMap.erase(game->GetId());
// Remove all sessions left in the game.
game->ResetComputerPlayerList();
game->RemoveAllSessions();
game->Exit();
// Notify all players.
boost::shared_ptr<NetPacket> packet = CreateNetPacketGameListUpdate(game->GetId(), GAME_MODE_CLOSED);
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game);
}
void
ServerLobbyThread::InternalRemovePlayer(unsigned playerId, unsigned errorCode)
{
boost::shared_ptr<SessionData> session = m_sessionManager.GetSessionByUniquePlayerId(playerId, true);
if (session)
SessionError(session, errorCode);
else {
// Remove player from game.
boost::shared_ptr<SessionData> session = m_gameSessionManager.GetSessionByUniquePlayerId(playerId);
if (session) {
boost::shared_ptr<ServerGame> tmpGame = session->GetGame();
if (tmpGame) {
tmpGame->RemovePlayer(playerId, errorCode);
}
}
}
}
void
ServerLobbyThread::InternalMutePlayerInGame(unsigned playerId)
{
boost::shared_ptr<SessionData> session = m_gameSessionManager.GetSessionByUniquePlayerId(playerId);
if (session) {
boost::shared_ptr<ServerGame> tmpGame = session->GetGame();
if (tmpGame) {
tmpGame->MutePlayer(playerId, true);
}
}
}
void
ServerLobbyThread::InternalResubscribeMsg(boost::shared_ptr<SessionData> session)
{
if (!session->WantsLobbyMsg()) {
session->SetWantsLobbyMsg();
SendPlayerList(session);
SendGameList(session);
// Send new statistics information.
/* boost::shared_ptr<NetPacket> packet(new NetPacket(NetPacket::Alloc));
packet->GetMsg()->present = PokerTHMessage_PR_statisticsMessage;
StatisticsMessage_t *netStatistics = &packet->GetMsg()->choice.statisticsMessage;
StatisticsData_t *data = (StatisticsData_t *)calloc(1, sizeof(struct StatisticsData));
data->statisticsType = statisticsType_statNumberOfPlayers;
data->statisticsValue = m_sessionManager.GetRawSessionCount() + m_gameSessionManager.GetRawSessionCount();
ASN_SEQUENCE_ADD(&netStatistics->statisticsData.list, data);
GetSender().Send(session, packet);*/
}
}
void
ServerLobbyThread::HandleReAddedSession(boost::shared_ptr<SessionData> session)
{
// Remove session from game session list.
m_gameSessionManager.RemoveSession(session->GetId());
if (m_sessionManager.GetRawSessionCount() <= SERVER_MAX_NUM_LOBBY_SESSIONS) {
// Set state (back) to established.
session->SetState(SessionData::Established);
session->SetGame(boost::shared_ptr<ServerGame>());
// Add session to lobby list.
m_sessionManager.AddSession(session);
} else {
// Gracefully close this session.
SessionError(session, ERR_NET_SERVER_FULL);
}
}
void
ServerLobbyThread::SessionTimeoutWarning(boost::shared_ptr<SessionData> session, unsigned remainingSec)
{
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_TimeoutWarningMessage);
TimeoutWarningMessage *netWarning = packet->GetMsg()->mutable_timeoutwarningmessage();
netWarning->set_timeoutreason(TimeoutWarningMessage::timeoutNoDataReceived);
netWarning->set_remainingseconds(remainingSec);
GetSender().Send(session, packet);
if (session->GetGame() && session->GetPlayerData()) {
session->GetGame()->MarkPlayerAsInactive(session->GetPlayerData()->GetUniqueId());
}
}
void
ServerLobbyThread::SessionError(boost::shared_ptr<SessionData> session, int errorCode)
{
if (session) {
if (errorCode == ERR_NET_PLAYER_KICKED || errorCode == ERR_NET_SESSION_TIMED_OUT) {
if (session->GetGame() && session->GetPlayerData()) {
session->GetGame()->MarkPlayerAsKicked(session->GetPlayerData()->GetUniqueId());
}
}
SendError(session, errorCode);
CloseSession(session);
}
}
void
ServerLobbyThread::SendError(boost::shared_ptr<SessionData> s, int errorCode)
{
2008-03-19 23:39:59 +00:00
LOG_VERBOSE("Sending error code " << errorCode << " to session #" << s->GetId() << ".");
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ErrorMessage);
ErrorMessage *netError = packet->GetMsg()->mutable_errormessage();
netError->set_errorreason(NetPacket::GameErrorToNetError(errorCode));
GetSender().Send(s, packet);
}
void
ServerLobbyThread::SendJoinGameFailed(boost::shared_ptr<SessionData> s, unsigned gameId, int reason)
{
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_JoinGameFailedMessage);
JoinGameFailedMessage *netJoinFailed = packet->GetMsg()->mutable_joingamefailedmessage();
netJoinFailed->set_gameid(gameId);
switch (reason) {
case NTF_NET_JOIN_GAME_FULL :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::gameIsFull);
break;
case NTF_NET_JOIN_ALREADY_RUNNING :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::gameIsRunning);
break;
case NTF_NET_JOIN_INVALID_PASSWORD :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::invalidPassword);
break;
case NTF_NET_JOIN_GUEST_FORBIDDEN :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::notAllowedAsGuest);
break;
case NTF_NET_JOIN_NOT_INVITED :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::notInvited);
break;
case NTF_NET_JOIN_GAME_NAME_IN_USE :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::gameNameInUse);
break;
case NTF_NET_JOIN_GAME_BAD_NAME :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::badGameName);
break;
case NTF_NET_JOIN_INVALID_SETTINGS :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::invalidSettings);
break;
case NTF_NET_JOIN_IP_BLOCKED :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::ipAddressBlocked);
break;
case NTF_NET_JOIN_REJOIN_FAILED :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::rejoinFailed);
break;
default :
netJoinFailed->set_joingamefailurereason(JoinGameFailedMessage::invalidGame);
break;
}
GetSender().Send(s, packet);
}
void
ServerLobbyThread::SendPlayerList(boost::shared_ptr<SessionData> s)
{
// Retrieve all player ids.
2009-08-15 14:08:48 +00:00
PlayerIdList idList(m_sessionManager.GetPlayerIdList(SessionData::Established));
PlayerIdList gameIdList(m_gameSessionManager.GetPlayerIdList(SessionData::Game));
idList.splice(idList.begin(), gameIdList);
// Send all player ids to client.
PlayerIdList::const_iterator i = idList.begin();
PlayerIdList::const_iterator end = idList.end();
while (i != end) {
GetSender().Send(s, CreateNetPacketPlayerListNew(*i));
++i;
}
}
void
ServerLobbyThread::SendGameList(boost::shared_ptr<SessionData> s)
{
GameMap::const_iterator game_i = m_gameMap.begin();
GameMap::const_iterator game_end = m_gameMap.end();
while (game_i != game_end) {
GetSender().Send(s, CreateNetPacketGameListNew(*game_i->second));
++game_i;
}
}
void
ServerLobbyThread::UpdateStatisticsNumberOfPlayers()
{
ServerStats stats;
// Get all logged-in sessions and all sessions within a game.
unsigned curNumberOfPlayersOnServer = m_sessionManager.GetEstablishedSessionCount() + m_gameSessionManager.GetRawSessionCount();
{
boost::mutex::scoped_lock lock(m_statMutex);
if (curNumberOfPlayersOnServer != m_statData.numberOfPlayersOnServer) {
m_statData.numberOfPlayersOnServer = stats.numberOfPlayersOnServer = curNumberOfPlayersOnServer;
if (curNumberOfPlayersOnServer > m_statData.maxPlayersLoggedIn)
m_statData.maxPlayersLoggedIn = curNumberOfPlayersOnServer;
m_statDataChanged = true;
}
}
// Do not send other stats than number of players for now.
//BroadcastStatisticsUpdate(stats);
}
void
ServerLobbyThread::BroadcastStatisticsUpdate(const ServerStats &stats)
{
if (stats.numberOfPlayersOnServer) {
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_StatisticsMessage);
StatisticsMessage *netStatistics = packet->GetMsg()->mutable_statisticsmessage();
StatisticsMessage::StatisticsData *data = netStatistics->add_statisticsdata();
data->set_statisticstype(StatisticsMessage::StatisticsData::statNumberOfPlayers);
data->set_statisticsvalue(m_sessionManager.GetRawSessionCount() + m_gameSessionManager.GetRawSessionCount());
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
m_gameSessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Game);
}
}
void
ServerLobbyThread::ReadStatisticsFile()
{
ifstream i(m_statisticsFileName.c_str(), ios_base::in);
if (!i.fail() && !i.eof()) {
boost::mutex::scoped_lock lock(m_statMutex);
do {
string statisticsType;
unsigned statisticsValue;
i >> statisticsType;
i >> statisticsValue;
if (statisticsType == SERVER_STATISTICS_STR_TOTAL_PLAYERS)
m_statData.totalPlayersEverLoggedIn = statisticsValue;
else if (statisticsType == SERVER_STATISTICS_STR_TOTAL_GAMES)
m_statData.totalGamesEverCreated = statisticsValue;
else if (statisticsType == SERVER_STATISTICS_STR_MAX_PLAYERS)
m_statData.maxPlayersLoggedIn = statisticsValue;
else if (statisticsType == SERVER_STATISTICS_STR_MAX_GAMES)
m_statData.maxGamesOpen = statisticsValue;
2008-04-01 21:44:05 +00:00
// other statistics are non-persistant and not read.
} while (!i.fail() && !i.eof());
m_statDataChanged = false;
}
}
void
ServerLobbyThread::TimerSaveStatisticsFile(const boost::system::error_code &ec)
{
if (!ec) {
LOG_VERBOSE("Saving statistics.");
boost::mutex::scoped_lock lock(m_statMutex);
if (m_statDataChanged) {
ofstream o(m_statisticsFileName.c_str(), ios_base::out | ios_base::trunc);
if (!o.fail()) {
o << SERVER_STATISTICS_STR_TOTAL_PLAYERS " " << m_statData.totalPlayersEverLoggedIn << endl;
o << SERVER_STATISTICS_STR_TOTAL_GAMES " " << m_statData.totalGamesEverCreated << endl;
o << SERVER_STATISTICS_STR_MAX_PLAYERS " " << m_statData.maxPlayersLoggedIn << endl;
o << SERVER_STATISTICS_STR_MAX_GAMES " " << m_statData.maxGamesOpen << endl;
o << SERVER_STATISTICS_STR_CUR_PLAYERS " " << m_statData.numberOfPlayersOnServer << endl;
o << SERVER_STATISTICS_STR_CUR_GAMES " " << m_statData.numberOfGamesOpen << endl;
m_statDataChanged = false;
}
}
2009-06-12 16:04:58 +00:00
// Restart timer
m_saveStatisticsTimer.expires_from_now(
boost::posix_time::seconds(SERVER_SAVE_STATISTICS_INTERVAL_SEC));
m_saveStatisticsTimer.async_wait(
boost::bind(
&ServerLobbyThread::TimerSaveStatisticsFile, shared_from_this(), boost::asio::placeholders::error));
}
}
ServerCallback &
ServerLobbyThread::GetCallback()
{
return m_gui;
}
ServerIrcBotCallback &
ServerLobbyThread::GetIrcBotCallback()
{
return m_ircBotCb;
}
2009-09-19 20:28:30 +00:00
InternalServerCallback &
ServerLobbyThread::GetSenderCallback()
{
2009-09-19 20:28:30 +00:00
assert(m_internalServerCallback.get());
return *m_internalServerCallback;
}
GuiInterface &
ServerLobbyThread::GetGui()
{
return m_gui;
}
unsigned
ServerLobbyThread::GetPlayerId(const string &name) const
{
unsigned playerId = 0;
boost::shared_ptr<SessionData> tmpSession(m_sessionManager.GetSessionByPlayerName(name));
if (!tmpSession)
tmpSession = m_gameSessionManager.GetSessionByPlayerName(name);
if (tmpSession && tmpSession->GetPlayerData())
playerId = tmpSession->GetPlayerData()->GetUniqueId();
return playerId;
}
boost::shared_ptr<NetPacket>
ServerLobbyThread::CreateNetPacketPlayerListNew(unsigned playerId)
{
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayerListMessage);
PlayerListMessage *netPlayerList = packet->GetMsg()->mutable_playerlistmessage();
netPlayerList->set_playerid(playerId);
netPlayerList->set_playerlistnotification(PlayerListMessage::playerListNew);
return packet;
}
boost::shared_ptr<NetPacket>
ServerLobbyThread::CreateNetPacketPlayerListLeft(unsigned playerId)
{
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayerListMessage);
PlayerListMessage *netPlayerList = packet->GetMsg()->mutable_playerlistmessage();
netPlayerList->set_playerid(playerId);
netPlayerList->set_playerlistnotification(PlayerListMessage::playerListLeft);
return packet;
}
boost::shared_ptr<NetPacket>
ServerLobbyThread::CreateNetPacketGameListNew(const ServerGame &game)
{
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListNewMessage);
GameListNewMessage *netGameList = packet->GetMsg()->mutable_gamelistnewmessage();
netGameList->set_gameid(game.GetId());
netGameList->set_adminplayerid(game.GetAdminPlayerId());
2012-09-23 23:19:53 +02:00
netGameList->set_gamemode(game.IsRunning() ? netGameStarted : netGameCreated);
NetPacket::SetGameData(game.GetGameData(), *netGameList->mutable_gameinfo());
netGameList->mutable_gameinfo()->set_gamename(game.GetName());
netGameList->set_isprivate(game.IsPasswordProtected());
PlayerIdList tmpList = game.GetPlayerIdList();
PlayerIdList::const_iterator i = tmpList.begin();
PlayerIdList::const_iterator end = tmpList.end();
while (i != end) {
netGameList->add_playerids(*i);
++i;
}
return packet;
}
boost::shared_ptr<NetPacket>
ServerLobbyThread::CreateNetPacketGameListUpdate(unsigned gameId, GameMode mode)
{
boost::shared_ptr<NetPacket> packet(new NetPacket);
packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameListUpdateMessage);
GameListUpdateMessage *netGameList = packet->GetMsg()->mutable_gamelistupdatemessage();
netGameList->set_gameid(gameId);
netGameList->set_gamemode(static_cast<NetGameMode>(mode));
return packet;
}
u_int32_t
ServerLobbyThread::GetRejoinGameIdForPlayer(const std::string &playerName, const std::string &guid, unsigned &outPlayerUniqueId)
{
u_int32_t retGameId = 0;
if (!guid.empty()) {
GameMap::iterator i = m_gameMap.begin();
GameMap::iterator end = m_gameMap.end();
while (i != end) {
boost::shared_ptr<ServerGame> tmpGame = i->second;
boost::shared_ptr<PlayerInterface> tmpPlayer = tmpGame->GetPlayerInterfaceFromGame(playerName);
if (tmpPlayer && tmpPlayer->getMyGuid() == guid && tmpPlayer->getMyCash() > 0) {
retGameId = tmpGame->GetId();
outPlayerUniqueId = tmpPlayer->getMyUniqueID();
break;
}
++i;
}
}
return retGameId;
}