ServerAddPlayerHandler.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/ServerAddPlayerHandler.h>
00022 #include <server/ServerConnectHandler.h>
00023 #include <server/ScorchedServer.h>
00024 #include <server/ScorchedServerUtil.h>
00025 #include <server/ServerState.h>
00026 #include <server/ServerChannelManager.h>
00027 #include <common/OptionsScorched.h>
00028 #include <common/OptionsTransient.h>
00029 #include <common/StatsLogger.h>
00030 #include <common/Logger.h>
00031 #include <common/Defines.h>
00032 #include <coms/ComsAddPlayerMessage.h>
00033 #include <coms/ComsPlayerStateMessage.h>
00034 #include <coms/ComsMessageSender.h>
00035 #include <net/NetLoopBack.h>
00036 #include <tankai/TankAIStore.h>
00037 #include <tank/TankModelStore.h>
00038 #include <tank/TankModelContainer.h>
00039 #include <tank/TankContainer.h>
00040 #include <tank/TankColorGenerator.h>
00041 #include <tank/TankState.h>
00042 #include <tank/TankAvatar.h>
00043 
00044 ServerAddPlayerHandler *ServerAddPlayerHandler::instance_ = 0;
00045 
00046 ServerAddPlayerHandler *ServerAddPlayerHandler::instance()
00047 {
00048         if (!instance_)
00049         {
00050                 instance_ = new ServerAddPlayerHandler;
00051         }
00052         return instance_;
00053 }
00054 
00055 ServerAddPlayerHandler::ServerAddPlayerHandler()
00056 {
00057         ScorchedServer::instance()->getComsMessageHandler().addHandler(
00058                 "ComsAddPlayerMessage",
00059                 this);
00060 }
00061 
00062 ServerAddPlayerHandler::~ServerAddPlayerHandler()
00063 {
00064 }
00065 
00066 bool ServerAddPlayerHandler::processMessage(NetMessage &netMessage,
00067         const char *messageType, NetBufferReader &reader)
00068 {
00069         ComsAddPlayerMessage message;
00070         if (!message.readMessage(reader)) return false;
00071 
00072         // Validate player
00073         unsigned int playerId = message.getPlayerId();
00074         Tank *tank = ScorchedServer::instance()->getTankContainer().getTankById(playerId);
00075         if (!tank || 
00076                 (tank->getState().getState() != TankState::sDead &&
00077                 tank->getState().getState() != TankState::sPending &&
00078                 tank->getState().getState() != TankState::sLoading &&
00079                 tank->getState().getState() != TankState::sInitializing))
00080         {
00081                 ServerChannelManager::instance()->sendText( 
00082                         ChannelText("info", "CHANGE_WHEN_DEAD", 
00083                         "Can only change tank when dead."),
00084                         netMessage.getDestinationId(), 
00085                         false);
00086                 return true;
00087         }
00088 
00089         // Add a computer player (if chosen and a single player match)
00090         if (0 != strcmp(message.getPlayerType(), "Human"))
00091         {
00092                 if (ScorchedServer::instance()->getGameState().getState() !=
00093                         ServerState::ServerStateTooFewPlayers)
00094                 {
00095                         ServerChannelManager::instance()->sendText( 
00096                                 ChannelText("info", "CHANGE_WHEN_STARTED", 
00097                                         "Can only change type before game starts."),
00098                                         netMessage.getDestinationId(),
00099                                         false);
00100                         return true;
00101                 }
00102 
00103                 // Only allow this on a single player game
00104 #ifdef S3D_SERVER
00105                 return true;
00106 #endif // #ifdef S3D_SERVER
00107 
00108                 // Check tank ai is valid
00109                 TankAI *ai = 
00110                         ScorchedServer::instance()->getTankAIs().
00111                         getAIByName(message.getPlayerType());
00112                 if (!ai) return true;
00113 
00114                 // Set the tank to have the ai
00115                 tank->setTankAI(ai->createCopy(tank));
00116                 tank->setDestinationId(0);
00117         }
00118         else
00119         {
00120                 tank->setDestinationId(netMessage.getDestinationId());
00121                 tank->setTankAI(0);
00122         }
00123 
00124         // Setup the new player
00125         LangString name(message.getPlayerName());
00126         filterName(tank, name);
00127 
00128 #ifdef S3D_SERVER
00129         // Tell this computer that a new tank has connected
00130         if (name != tank->getTargetName())
00131         {
00132                 Logger::log(S3D::formatStringBuffer(
00133                         "Player playing dest=\"%i\" id=\"%i\" \"%s\"->\"%s\"",
00134                         tank->getDestinationId(), tank->getPlayerId(),
00135                         tank->getCStrName().c_str(), name.c_str()));
00136 
00137                 ServerChannelManager::instance()->sendText( 
00138                         ChannelText("info",
00139                                 "PLAYER_NAME_CHANGE",
00140                                 "Player \"{0}\" changed name to \"{1}\"",
00141                                 tank->getTargetName(), name),
00142                         true);
00143         }
00144 #endif // #ifdef S3D_SERVER
00145 
00146         tank->setName(name);
00147 
00148         // Player has set a new color
00149         if (tank->getTeam() == 0 &&
00150                 message.getPlayerColor() != tank->getColor())
00151         {
00152                 // Check the color is not already in use
00153                 std::map<unsigned int, Tank *> &tanks = 
00154                         ScorchedServer::instance()->getTankContainer().getPlayingTanks();
00155                 if (TankColorGenerator::instance()->colorAvailable(
00156                         message.getPlayerColor(), tanks, tank))
00157                 {
00158                         // Set this color
00159                         tank->setColor(message.getPlayerColor());
00160                 }
00161         }
00162 
00163         bool noAvatar = !tank->getAvatar().getName()[0] 
00164                 && message.getPlayerIconName()[0];
00165         if (noAvatar) // Currently we can only set the avatar once
00166         {
00167                 if (message.getPlayerIcon().getBufferUsed() <=
00168                         (unsigned) ScorchedServer::instance()->getOptionsGame().getMaxAvatarSize())
00169                 {
00170                         tank->getAvatar().setFromBuffer(
00171                                 message.getPlayerIconName(),
00172                                 message.getPlayerIcon());
00173         
00174                         // Send a new add message to all clients (this contains the avatar)
00175                         // the client will not add the player but will update the avatar instead
00176                         // Note: this can be removed if we ever have enough bandwidth to send
00177                         // the avatar in each state message along with the rest of the tanks
00178                         // attributes.
00179                         ComsMessageSender::sendToAllConnectedClients(message);
00180                 }
00181         }
00182 
00183         // Tell the logger about a new tank
00184         StatsLogger::instance()->tankJoined(tank);
00185 
00186 #ifdef S3D_SERVER
00187         {
00188                 StatsLogger::TankRank rank = StatsLogger::instance()->tankRank(tank);
00189                 if (rank.rank >= 0)
00190                 {
00191                         ServerChannelManager::instance()->sendText( 
00192                                 ChannelText("info",
00193                                         "WELCOME_BACK",
00194                                         "Welcome back {0}, you are ranked {1}",
00195                                         tank->getTargetName(), rank.rank),
00196                                 true);
00197                 }
00198 
00199                 if (tank->getState().getSpectator())
00200                 {
00201                         ServerChannelManager::instance()->sendText( 
00202                                 ChannelText("info",
00203                                         "PLAYER_PLAYING",
00204                                         "Player playing \"{0}\"",
00205                                         tank->getTargetName()),
00206                                 true);
00207 
00208                         if (ScorchedServer::instance()->getGameState().getState() == 
00209                                 ServerState::ServerStateStarting)
00210                         {
00211                                 // Reset the starting timer
00212                                 ScorchedServer::instance()->getGameState().stimulate(
00213                                         ServerState::ServerStimulusStarting);
00214                         }
00215                 }
00216         }
00217 #endif // #ifdef S3D_SERVER
00218 
00219         // Make sure the tank state is as we expected
00220         // This also fixes setting the state after loading 
00221         // a saved game
00222         tank->getState().setSpectator(false);
00223 
00224         // Choose a team (if applicable)
00225         if (ScorchedServer::instance()->getOptionsGame().getTeams() > 1)
00226         {
00227                 if (message.getPlayerTeam() > 0 && message.getPlayerTeam() <=
00228                         (unsigned int) ScorchedServer::instance()->getOptionsGame().getTeams())
00229                 {
00230                         tank->setTeam(message.getPlayerTeam());
00231                 }
00232                 else
00233                 {
00234                         tank->setTeam(ScorchedServer::instance()->getOptionsTransient().getLeastUsedTeam(
00235                                 ScorchedServer::instance()->getTankContainer()));
00236                 }
00237         }
00238 
00239         // Make sure the model is available and for the correct team
00240         // Do this AFTER the team has been set
00241         TankModel *tankModel = 
00242                 ScorchedServer::instance()->getTankModels().
00243                         getModelByName(message.getModelName(), 
00244                                 tank->getTeam(),
00245                                 tank->isTemp());
00246         tank->getModelContainer().setTankModelName(
00247                 tankModel->getName(), message.getModelName(), tankModel->getTypeName());
00248 
00249         // If we are in a waiting for players state then we can
00250         // send the state of these new players
00251         if (ScorchedServer::instance()->getGameState().getState() == ServerState::ServerStateTooFewPlayers ||
00252                 ScorchedServer::instance()->getGameState().getState() == ServerState::ServerStateStarting)
00253         {
00254                 ComsPlayerStateMessage message(false, false);
00255                 ComsMessageSender::sendToAllConnectedClients(message);
00256         }
00257         return true;
00258 }
00259 
00260 bool ServerAddPlayerHandler::filterName(Tank *tank,
00261         LangString &sentname)
00262 {
00263         LangString originalname = sentname;
00264 
00265         // Remove spaces from the front and end of the name 
00266         LangStringUtil::trim(sentname);
00267 
00268         // Ensure this name does not have any "bad" words in it
00269         ScorchedServerUtil::instance()->textFilter.filterString(sentname);
00270 
00271         // Remove unwanted characters from middle
00272         for (unsigned int *c = (unsigned int *) sentname.c_str(); *c;  c++)
00273         {
00274                 if (*c == '\"') *c = '\'';
00275                 else if (*c == ']') *c = ')';
00276                 else if (*c == '[') *c = '(';
00277                 else if (*c == '%') *c = '$'; // Save problems with special chars
00278                 if (!ScorchedServer::instance()->getOptionsGame().getAllowMultiLingualNames())
00279                 {
00280                         if (*c > 127) *c = '?';
00281                 }
00282         }
00283 
00284         // Ensure this name does not have the bot name in it
00285         LangString botPrefix = 
00286                 LANG_STRING(ScorchedServer::instance()->getOptionsGame().getBotNamePrefix());
00287         unsigned int *botPrefixPos = LangStringUtil::stristr(sentname.c_str(), botPrefix);
00288         if (botPrefixPos)
00289         {
00290                 for (int i=0; i<(int) botPrefix.size(); i++, botPrefixPos++)
00291                 {
00292                         (*botPrefixPos) = '*';
00293                 }
00294         }               
00295 
00296         // Check the client provides a name with a least 1 char in it
00297         // and the name is less than 16 chars
00298         if (sentname.size() == 0) sentname = LANG_STRING("NoName");
00299         if (sentname.size() > 22) sentname = sentname.substr(0, 22);
00300 
00301         // Make sure no-one has the same name
00302         for (;;)
00303         {
00304                 bool found = false;
00305                 std::map<unsigned int, Tank *>::iterator mainitor;
00306                 std::map<unsigned int, Tank *> tanks = 
00307                         ScorchedServer::instance()->getTankContainer().getAllTanks();
00308                 for (mainitor = tanks.begin();
00309                         mainitor != tanks.end();
00310                         mainitor++)
00311                 {
00312                         Tank *currentTank = (*mainitor).second;
00313                         if (currentTank->getTargetName() == sentname &&
00314                                 tank != currentTank) 
00315                         {
00316                                 found = true;
00317                                 break;
00318                         }
00319                 }
00320 
00321                 if (!found) break;
00322                 sentname += LANG_STRING("(2)");
00323         }
00324 
00325         // Make sure that no-one else has the same registered name
00326         // except the prefered user that has this name
00327         if (ScorchedServer::instance()->getOptionsGame().getRegisteredUserNames())
00328         {
00329                 ServerAuthHandler *authHandler =
00330                         ScorchedServerUtil::instance()->getAuthHandler();
00331                 if (authHandler)
00332                 {
00333                         while (!authHandler->authenticateUserName(tank->getUniqueId(),
00334                                 sentname))
00335                         {
00336                                 sentname += LANG_STRING("(2)");
00337                         }
00338                 }
00339         }
00340 
00341         return (sentname != originalname);
00342 }

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