ServerFileServer.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/ServerFileServer.h>
00022 #include <server/ScorchedServer.h>
00023 #include <server/ServerCommon.h>
00024 #include <tank/TankContainer.h>
00025 #include <tank/TankState.h>
00026 #include <tank/TankMod.h>
00027 #include <common/OptionsScorched.h>
00028 #include <common/Defines.h>
00029 #include <coms/ComsMessageSender.h>
00030 #include <coms/ComsInitializeMessage.h>
00031 #include <engine/ModFiles.h>
00032 #include <time.h>
00033 
00034 ServerFileServer *ServerFileServer::instance_ = 0;
00035 
00036 ServerFileServer *ServerFileServer::instance()
00037 {
00038         if (!instance_)
00039         {
00040                 instance_ = new ServerFileServer;
00041         }
00042         return instance_;
00043 }
00044 
00045 ServerFileServer::ServerFileServer() : lastTime_(0), bytesSent_(0)
00046 {
00047 }
00048 
00049 ServerFileServer::~ServerFileServer()
00050 {
00051 }
00052 
00053 void ServerFileServer::simulate(float timeDifference)
00054 {
00055         int downloadCount = 0;
00056 
00057         // TODO this should really be done on a per destination id basis
00058         // not on a per tank basis!!
00059 
00060         // Check how many people are currently downloading
00061         // also check for any that have finished downloading
00062         std::map<unsigned int, Tank *> tanks = 
00063                 ScorchedServer::instance()->getTankContainer().getPlayingTanks();
00064         std::map<unsigned int, Tank *>::iterator itor;
00065         for (itor = tanks.begin();
00066                 itor != tanks.end();
00067                 itor++)
00068         {
00069                 Tank *tank = (*itor).second;
00070                 if (tank->getState().getState() == TankState::sLoading)
00071                 {
00072                         // Does this tank have any more files to send
00073                         if (!tank->getMod().getFiles().empty())
00074                         {
00075                                 // Yes, 1 more tank to send to
00076                                 downloadCount++;
00077                         }
00078                         else
00079                         {
00080                                 // Check if this tank had had a chance to get its mod files
00081                                 if (tank->getMod().getInit())
00082                                 {
00083                                         // Set this tank to finished
00084                                         tank->getState().setState(TankState::sInitializing);
00085 
00086                                         // If all tanks at this destination have finished send init message
00087                                         bool allComplete = true;
00088                                         std::map<unsigned int, Tank *>::iterator seconditor;
00089                                         for (seconditor = tanks.begin();
00090                                                 seconditor != tanks.end();
00091                                                 seconditor++)
00092                                         {
00093                                                 Tank *secondtank = (*seconditor).second;
00094                                                 if (secondtank->getDestinationId() == tank->getDestinationId())
00095                                                 {
00096                                                         if (secondtank->getState().getState() ==
00097                                                                 TankState::sLoading)
00098                                                         {
00099                                                                 allComplete = false;
00100                                                                 break;
00101                                                         }
00102                                                 }
00103                                         }
00104 
00105                                         // If this tank is not initialized make it initialized
00106                                         if (allComplete)
00107                                         {
00108                                                 // Tell this destination to start initializing
00109                                                 ComsInitializeMessage initMessage;
00110                                                 ComsMessageSender::sendToSingleClient(initMessage,
00111                                                         tank->getDestinationId());
00112                                         }
00113                                 }
00114                         }
00115                 }
00116         }
00117 
00118         // Do we allow file downloads
00119         if (ScorchedServer::instance()->getOptionsGame().getModDownloadSpeed() == 0)
00120         {
00121                 return;
00122         }
00123 
00124         // If no people are downloading then there is nothing to do
00125         if (downloadCount == 0) return;
00126 
00127         // Cacluate the maximum amount that can be downloaded by each
00128         // client per second
00129         int maxDownloadPerClient = 
00130                 ScorchedServer::instance()->getOptionsGame().getModDownloadSpeed() /
00131                 downloadCount;
00132 
00133         // Check if this is the next second
00134         unsigned int theTime = (unsigned int) time(0);
00135         if (theTime > lastTime_)
00136         {
00137                 // If so reset the amount that can be sent by this server
00138                 lastTime_ = theTime;
00139                 bytesSent_ = 0;
00140 
00141                 // Reset the sent state for each client
00142                 for (itor = tanks.begin();
00143                         itor != tanks.end();
00144                         itor++)
00145                 {
00146                         Tank *tank = (*itor).second;
00147                         if (tank->getState().getState() == TankState::sLoading)
00148                         {
00149                                 tank->getMod().setSent(false);
00150                         }
00151                 }
00152         }
00153 
00154         // Send bytes to each ready client
00155         for (itor = tanks.begin();
00156                 itor != tanks.end();
00157                 itor++)
00158         {
00159                 // Make sure we have not sent too much this second already
00160                 if (bytesSent_ + maxDownloadPerClient > 
00161                         (unsigned int) ScorchedServer::instance()->getOptionsGame().getModDownloadSpeed() + 5)
00162                 {
00163                         return;
00164                 }
00165 
00166                 Tank *tank = (*itor).second;
00167                 if (tank->getState().getState() == TankState::sLoading)
00168                 {
00169                         // Check if the client is ready to recieve more
00170                         // bytes and there is some to send and
00171                         // we have not sent to this client this second
00172                         if (tank->getMod().getReadyToReceive() &&
00173                                 !tank->getMod().getFiles().empty() &&
00174                                 !tank->getMod().getSent())
00175                         {
00176                                 // Send bytes to this tank
00177                                 tank->getMod().setSent(true);
00178                                 tank->getMod().setReadyToReceive(false);
00179                                 bytesSent_ += maxDownloadPerClient;
00180 
00181                                 sendBytes(tank, maxDownloadPerClient);
00182                         }
00183                 }
00184         }
00185 }
00186 
00187 void ServerFileServer::sendBytes(Tank *tank, unsigned int size)
00188 {
00189         ComsFileMessage message;
00190         // Fill up the message with files, until it is full
00191         while (size > 0)
00192         {
00193                 unsigned int bytesSent = 0;
00194                 if (!sendNextFile(message, tank, size, bytesSent)) break;
00195                 size -= bytesSent;
00196         }
00197         // Add any empty file name to signal end of files for this message
00198         message.fileBuffer.addToBuffer("");
00199 
00200         // Send the message to the client
00201         ComsMessageSender::sendToSingleClient(message, 
00202                 tank->getDestinationId());
00203 
00204 }
00205 
00206 bool ServerFileServer::sendNextFile(ComsFileMessage &message,
00207         Tank *tank, unsigned int size,
00208         unsigned int &bytesSent)
00209 {
00210         // Get the next file to send
00211         std::list<ModIdentifierEntry> &files = 
00212                 tank->getMod().getFiles();
00213         if (files.empty()) return false;
00214         ModIdentifierEntry &entry = files.front();
00215 
00216         // Find the next file in the modfiles
00217         std::map<std::string, ModFileEntry *> &modfiles =
00218                 ScorchedServer::instance()->getModFiles().getFiles();
00219         std::map<std::string, ModFileEntry *>::iterator findItor =
00220                 modfiles.find(entry.fileName);
00221         DIALOG_ASSERT(findItor != modfiles.end());
00222         ModFileEntry *modentry = (*findItor).second;
00223 
00224     // Check how much still needs to be sent
00225         unsigned int sizeSent = entry.length;
00226         unsigned int sizeLeftToSend = modentry->getCompressedSize() - sizeSent;
00227         unsigned int sizeToSend = MIN(sizeLeftToSend, size);
00228         unsigned int bytesLeft = tank->getMod().getTotalLeft();
00229         bool firstChunk = (sizeSent == 0);
00230         bool lastChunk = (sizeToSend == sizeLeftToSend);
00231 
00232         // Add the bytes to the buffer
00233         message.fileBuffer.addToBuffer(modentry->getFileName());
00234         message.fileBuffer.addToBuffer(firstChunk);
00235         message.fileBuffer.addToBuffer(lastChunk);
00236         message.fileBuffer.addToBuffer(bytesLeft);
00237         message.fileBuffer.addToBuffer(modentry->getCompressedSize());
00238         message.fileBuffer.addToBuffer(modentry->getUncompressedSize());
00239         message.fileBuffer.addToBuffer(modentry->getCompressedCrc());
00240         message.fileBuffer.addToBuffer(sizeToSend);
00241         message.fileBuffer.addDataToBuffer(modentry->getCompressedBytes() + entry.length,
00242                 sizeToSend);
00243 
00244         // Update how much we have sent
00245         entry.length += sizeToSend;
00246         tank->getMod().setTotalLeft(tank->getMod().getTotalLeft() - sizeToSend);
00247 
00248         // Have we sent the whole file
00249         if (sizeToSend == sizeLeftToSend)
00250         {
00251                 // If so remove the file from the list that
00252                 // still needs to be sent
00253                 std::string fileName = modentry->getFileName();
00254                 tank->getMod().rmFile(fileName.c_str());
00255         }
00256 
00257         bytesSent = sizeToSend;
00258         return true;
00259 }
00260 

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