ServerConnectAuthHandler.cpp

Go to the documentation of this file.
00001 ////////////////////////////////////////////////////////////////////////////////
00002 //    Scorched3D (c) 2000-2009
00003 //
00004 //    This file is part of Scorched3D.
00005 //
00006 //    Scorched3D is free software; you can redistribute it and/or modify
00007 //    it under the terms of the GNU General Public License as published by
00008 //    the Free Software Foundation; either version 2 of the License, or
00009 //    (at your option) any later version.
00010 //
00011 //    Scorched3D is distributed in the hope that it will be useful,
00012 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //    GNU General Public License for more details.
00015 //
00016 //    You should have received a copy of the GNU General Public License
00017 //    along with Scorched3D; if not, write to the Free Software
00018 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 ////////////////////////////////////////////////////////////////////////////////
00020 
00021 #include <server/ServerConnectAuthHandler.h>
00022 #include <server/ServerConnectHandler.h>
00023 #include <server/ServerChannelManager.h>
00024 #include <server/ServerState.h>
00025 #include <server/ServerBanned.h>
00026 #include <server/ScorchedServer.h>
00027 #include <server/ScorchedServerUtil.h>
00028 #include <server/TurnController.h>
00029 #include <server/ServerCommon.h>
00030 #include <tank/TankColorGenerator.h>
00031 #include <tank/TankContainer.h>
00032 #include <tank/TankModelContainer.h>
00033 #include <tank/TankAvatar.h>
00034 #include <tank/TankState.h>
00035 #include <tank/TankScore.h>
00036 #include <tankai/TankAIAdder.h>
00037 #include <tankai/TankAIStrings.h>
00038 #include <common/Defines.h>
00039 #include <common/FileLines.h>
00040 #include <common/OptionsScorched.h>
00041 #include <common/StatsLogger.h>
00042 #include <common/Logger.h>
00043 #include <net/NetInterface.h>
00044 #include <coms/ComsPlayerStateMessage.h>
00045 #include <coms/ComsAddPlayerMessage.h>
00046 #include <coms/ComsConnectAcceptMessage.h>
00047 #include <coms/ComsConnectMessage.h>
00048 #include <coms/ComsMessageSender.h>
00049 
00050 ServerConnectAuthHandler *ServerConnectAuthHandler::instance_ = 0;
00051 
00052 ServerConnectAuthHandler *ServerConnectAuthHandler::instance()
00053 {
00054         if (!instance_)
00055         {
00056                 instance_ = new ServerConnectAuthHandler;
00057         }
00058         return instance_;
00059 }
00060 
00061 ServerConnectAuthHandler::ServerConnectAuthHandler()
00062 {
00063         ScorchedServer::instance()->getComsMessageHandler().addHandler(
00064                 "ComsConnectAuthMessage",
00065                 this);
00066 }
00067 
00068 ServerConnectAuthHandler::~ServerConnectAuthHandler()
00069 {
00070 }
00071 
00072 bool ServerConnectAuthHandler::processMessage(
00073         NetMessage &netMessage,
00074         const char *messageType, NetBufferReader &reader)
00075 {
00076         unsigned int destinationId = netMessage.getDestinationId();
00077         unsigned int ipAddress = netMessage.getIpAddress();
00078 
00079         // Check for acceptance bassed on standard checks
00080         if (!ServerConnectHandler::instance()->checkStandardParams(destinationId, ipAddress)) return true;
00081 
00082         // Decode the auth connect message
00083         ComsConnectAuthMessage message;
00084         if (!message.readMessage(reader))
00085         {
00086                 ServerCommon::serverLog("Invalid auth message format");
00087                 ServerCommon::kickDestination(destinationId);
00088                 return true;
00089         }
00090 
00091         // Check player availability
00092         if (message.getNoPlayers() > 
00093                 (unsigned int)
00094                 (ScorchedServer::instance()->getOptionsGame().getNoMaxPlayers() -
00095                 ScorchedServer::instance()->getTankContainer().getNoOfTanks()))
00096         {
00097                 std::string kickMessage = 
00098                         S3D::formatStringBuffer(
00099                         "--------------------------------------------------\n"
00100                         "This server is full, you cannot join!\n"
00101                         "--------------------------------------------------");
00102                 ServerCommon::serverLog("Server full, kicking");
00103                 ServerCommon::kickDestination(destinationId, kickMessage);
00104                 return true;
00105         }
00106 
00107         // Auth handler, make sure that only prefered players can connect
00108         // DO FIRST AS THE HANDLER MAY ALTER THE MESSAGE
00109         ServerAuthHandler *authHandler =
00110                 ScorchedServerUtil::instance()->getAuthHandler();
00111         if (authHandler)
00112         {
00113                 std::string resultMessage;
00114                 if (!authHandler->authenticateUser(message, resultMessage))
00115                 {
00116                         std::string kickMessage = 
00117                                 S3D::formatStringBuffer(
00118                                 "--------------------------------------------------\n"
00119                                 "%s"
00120                                 "Connection failed.\n"
00121                                 "--------------------------------------------------",
00122                                 resultMessage.c_str());
00123                         Logger::log(S3D::formatStringBuffer("User failed authentication \"%s\"",
00124                                 message.getUserName()));
00125 
00126                         ServerCommon::kickDestination(destinationId, kickMessage);                      
00127                         return true;
00128                 }
00129         }
00130 
00131         std::string uniqueId = message.getUniqueId();
00132         if (!uniqueId.c_str()[0]) // No ID
00133         {
00134                 // Generate the players unique id (if we need to)
00135                 //
00136                 // This will only actualy generate an id when using a persistent
00137                 // stats logger as this is the only time we can be sure of not
00138                 // giving out duplicate ids.
00139                 //
00140                 uniqueId = StatsLogger::instance()->allocateId();
00141         }
00142         std::string SUid = message.getSUI(); //request for SUI
00143 
00144         // Get compat ver
00145         unsigned int compatVer = message.getCompatabilityVer();
00146         // TODO send back proper version message
00147 
00148         // Check if this unique id has been banned
00149         // Do after auth handler as this may update uniqueid
00150         ServerBanned::BannedType type = 
00151                 ScorchedServerUtil::instance()->bannedPlayers.getBanned(uniqueId.c_str(), SUid.c_str());
00152         if (type == ServerBanned::Banned)
00153         {
00154                 Logger::log(S3D::formatStringBuffer("Banned uniqueid/suid connection from destination \"%i\"", 
00155                         destinationId));
00156                 ServerCommon::kickDestination(destinationId);
00157                 return true;
00158         }
00159 
00160         // Check that this unique id has not already connected (if unique ids are in use)
00161         if (!ScorchedServer::instance()->getOptionsGame().getAllowSameUniqueId())
00162         {
00163                 std::map<unsigned int, Tank *> &playingTanks = 
00164                         ScorchedServer::instance()->getTankContainer().getPlayingTanks();
00165                 std::map<unsigned int, Tank *>::iterator playingItor;
00166                 for (playingItor = playingTanks.begin();
00167                         playingItor != playingTanks.end();
00168                         playingItor++)
00169                 {
00170                         Tank *current = (*playingItor).second;
00171 
00172                         if (uniqueId.c_str()[0])
00173                         {
00174                                 if (0 == strcmp(current->getUniqueId(), uniqueId.c_str()))
00175                                 {
00176                                         Logger::log(S3D::formatStringBuffer("Duplicate uniqueid connection from destination \"%i\"", 
00177                                                 destinationId));
00178                                         ServerCommon::kickDestination(destinationId);
00179                                         return true;
00180                                 }
00181                         }
00182                         if (SUid.c_str()[0])
00183                         {
00184                                 if (0 == strcmp(current->getSUI(), SUid.c_str()))
00185                                 {
00186                                         Logger::log(S3D::formatStringBuffer("Duplicate SUI connection from destination \"%i\"", 
00187                                                 destinationId));
00188                                         ServerCommon::kickDestination(destinationId);
00189                                         return true;
00190                                 }
00191                         }
00192                 }
00193         }
00194 
00195         // Send the connection accepted message to the client
00196         ComsConnectAcceptMessage acceptMessage(
00197                 destinationId,
00198                 ScorchedServer::instance()->getOptionsGame().getServerName(),
00199                 ScorchedServer::instance()->getOptionsGame().getPublishAddress(),
00200                 uniqueId.c_str());
00201         // Add the connection png
00202 #ifdef S3D_SERVER
00203         {
00204                 std::string fileName = 
00205                         S3D::getSettingsFile(S3D::formatStringBuffer("icon-%i.png",
00206                                 ScorchedServer::instance()->getOptionsGame().getPortNo()));
00207                 FILE *in = fopen(fileName.c_str(), "rb");
00208                 if (in)
00209                 {
00210                         acceptMessage.getServerPng().reset();
00211                         unsigned char readBuf[512];
00212                         while (unsigned int size = fread(readBuf, sizeof(unsigned char), 512, in))
00213                         {
00214                                 acceptMessage.getServerPng().addDataToBuffer(readBuf, size);
00215                         }
00216                         fclose(in);
00217                 }
00218         }
00219 #endif
00220         if (!ComsMessageSender::sendToSingleClient(acceptMessage, destinationId))
00221         {
00222                 Logger::log(S3D::formatStringBuffer(
00223                         "Failed to send accept to client \"%i\"",
00224                         destinationId));
00225                 ServerCommon::kickDestination(destinationId);
00226                 return true;
00227         }
00228 
00229         // Send all current tanks to the new client
00230         std::map<unsigned int, Tank *>::iterator itor;
00231         std::map<unsigned int, Tank *> &tankList = 
00232                 ScorchedServer::instance()->getTankContainer().getPlayingTanks();
00233         for (itor = tankList.begin();
00234                 itor != tankList.end();
00235                 itor++)
00236         {
00237                 Tank *tank = (*itor).second;
00238 
00239                 ComsAddPlayerMessage oldPlayerMessage(
00240                         tank->getPlayerId(),
00241                         tank->getTargetName(),
00242                         tank->getColor(),
00243                         tank->getModelContainer().getTankModelName(),
00244                         tank->getModelContainer().getTankTypeName(),
00245                         tank->getDestinationId(),
00246                         tank->getTeam(),
00247                         ""); 
00248                 oldPlayerMessage.setPlayerIconName(tank->getAvatar().getName());
00249                 oldPlayerMessage.getPlayerIcon().addDataToBuffer(
00250                         tank->getAvatar().getFile().getBuffer(),
00251                         tank->getAvatar().getFile().getBufferUsed());
00252                 ComsMessageSender::sendToSingleClient(oldPlayerMessage, destinationId);
00253         }
00254 
00255         // Add all the new tanks
00256         for (unsigned int i=0; i<message.getNoPlayers(); i++)
00257         {
00258                 addNextTank(destinationId,
00259                         ipAddress,      
00260                         uniqueId.c_str(),
00261                         SUid.c_str(),
00262                         message.getHostDesc(),
00263                         false);
00264         }
00265 
00266         // For the single player game
00267         // Add a spectator that will always remain a spectator
00268         // this is so if we only have computer players we still
00269         // send messages to them
00270 #ifndef S3D_SERVER
00271         {
00272                 addNextTank(destinationId,
00273                         ipAddress,
00274                         uniqueId.c_str(),
00275                         SUid.c_str(),
00276                         message.getHostDesc(),
00277                         true);
00278         }
00279 #endif
00280 
00281         // Send the state of all the currently connect clients
00282         ComsPlayerStateMessage comsPlayerStateMessage(false, false);
00283         if (!ComsMessageSender::sendToSingleClient(
00284                 comsPlayerStateMessage, destinationId)) return false;
00285 
00286         return true;
00287 }
00288 
00289 void ServerConnectAuthHandler::addNextTank(unsigned int destinationId,
00290         unsigned int ipAddress,
00291         const char *sentUniqueId,
00292         const char *sentSUI,
00293         const char *sentHostDesc,
00294         bool extraSpectator)
00295 {
00296         // The player has connected
00297         Vector color;
00298         unsigned int tankId = 0;
00299         LangString playerName;
00300 
00301         if (extraSpectator)
00302         {
00303                 tankId = TargetID::SPEC_TANK_ID;
00304                 playerName = LANG_STRING("Spectator");
00305                 color = Vector(0.7f, 0.7f, 0.7f);
00306         }
00307         else
00308         {
00309                 playerName = LANG_STRING(TankAIStrings::instance()->getPlayerName());
00310                 color = TankColorGenerator::instance()->getNextColor(
00311                         ScorchedServer::instance()->getTankContainer().getPlayingTanks());
00312                 tankId = TankAIAdder::getNextTankId(
00313                         sentUniqueId,
00314                         ScorchedServer::instance()->getContext());
00315         }
00316 
00317         // Make sure host desc does not contain \"
00318         for (char *h=(char *)sentHostDesc; *h; h++) if (*h == '\"') *h=' ';
00319         
00320         // Create the new tank 
00321         Tank *tank = new Tank(
00322                 ScorchedServer::instance()->getContext(),
00323                 tankId,
00324                 destinationId,
00325                 playerName,
00326                 color,
00327                 "Random",
00328                 "none"); // The model, this will be turned into a "proper" model in the player choice dialog
00329         tank->setUniqueId(sentUniqueId);
00330         tank->setSUI(sentSUI);
00331         tank->setIpAddress(ipAddress);
00332         tank->setHostDesc(sentHostDesc);
00333         tank->getState().setSpectator(true);
00334 
00335         // Use the stats name if stats are enabled and the player has one
00336         std::list<std::string> aliases  = 
00337                 StatsLogger::instance()->getAliases(tank->getUniqueId());
00338         if (!aliases.empty())
00339         {
00340                 LangString alias = LANG_STRING(aliases.front());
00341                 tank->setName(alias);
00342         }
00343 
00344         // Add the tank to the list of tanks
00345         ScorchedServer::instance()->getTankContainer().addTank(tank);
00346 
00347         // Tell the clients to create this tank
00348         ComsAddPlayerMessage addPlayerMessage(
00349                 tank->getPlayerId(),
00350                 tank->getTargetName(),
00351                 tank->getColor(),
00352                 tank->getModelContainer().getTankModelName(),
00353                 tank->getModelContainer().getTankTypeName(),
00354                 tank->getDestinationId(),
00355                 tank->getTeam(),
00356                 "");
00357         ComsMessageSender::sendToAllConnectedClients(addPlayerMessage);
00358 
00359         // Tell this computer that a new tank has connected
00360 #ifdef S3D_SERVER
00361         {
00362                 // Add to dialog
00363                 Logger::log(S3D::formatStringBuffer("Player connected dest=\"%i\" id=\"%i\" name=\"%s\" unique=[%s] SUI=[%s]",
00364                         tank->getDestinationId(),
00365                         tank->getPlayerId(),
00366                         tank->getCStrName().c_str(),
00367                         tank->getUniqueId(),
00368                         tank->getSUI()));
00369 
00370                 ServerChannelManager::instance()->sendText(
00371                         ChannelText("info", 
00372                                 "PLAYER_CONNECTED",
00373                                 "Player connected \"{0}\"",
00374                                 tank->getTargetName()),
00375                         true);
00376         }
00377 #endif
00378 
00379         // Add this tank to stats
00380         StatsLogger::instance()->tankConnected(tank);
00381         
00382         // Check if admin muted
00383         if (ipAddress != 0)
00384         {
00385                 ServerBanned::BannedType type = 
00386                         ScorchedServerUtil::instance()->bannedPlayers.getBanned(tank->getUniqueId(), tank->getSUI());
00387                 if (type == ServerBanned::Muted)        
00388                 {
00389                         tank->getState().setMuted(true);
00390                         ServerChannelManager::instance()->sendText( 
00391                                 ChannelText("admin", 
00392                                         "PLAYER_ADMIN_MUTED", 
00393                                         "Player admin muted \"{0}\"",
00394                                         tank->getTargetName()),
00395                                         true);
00396                 }
00397                 else if (type == ServerBanned::Flagged)
00398                 {
00399                         ServerChannelManager::instance()->sendText( 
00400                                 ChannelText("admin",
00401                                         "PLAYER_ADMIN_FLAGGED",
00402                                         "Player admin flagged \"{0}\"",
00403                                         tank->getTargetName()),
00404                                         true);
00405                 }
00406         }
00407 }

Generated on Mon Feb 16 15:14:53 2009 for Scorched3D by  doxygen 1.5.3