1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2011
3 //
4 // This file is part of Scorched3D.
5 //
6 // Scorched3D is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // Scorched3D is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20
21 #include <server/ServerHaveModFilesHandler.h>
22 #include <server/ServerChannelManager.h>
23 #include <server/ServerDestinations.h>
24 #include <server/ScorchedServer.h>
25 #include <server/ServerCommon.h>
26 #include <engine/ModFiles.h>
27 #include <coms/ComsHaveModFilesMessage.h>
28 #include <common/OptionsScorched.h>
29 #include <common/Defines.h>
30 #include <common/Logger.h>
31 #include <time.h>
32
ServerHaveModFilesHandler(ComsMessageHandler & comsMessageHandler)33 ServerHaveModFilesHandler::ServerHaveModFilesHandler(ComsMessageHandler &comsMessageHandler)
34 {
35 comsMessageHandler.addHandler(
36 ComsHaveModFilesMessage::ComsHaveModFilesMessageType,
37 this);
38 }
39
~ServerHaveModFilesHandler()40 ServerHaveModFilesHandler::~ServerHaveModFilesHandler()
41 {
42 }
43
processMessage(NetMessage & netMessage,const char * messageType,NetBufferReader & reader)44 bool ServerHaveModFilesHandler::processMessage(
45 NetMessage &netMessage,
46 const char *messageType,
47 NetBufferReader &reader)
48 {
49 ComsHaveModFilesMessage message;
50 if (!message.readMessage(reader)) return false;
51
52 unsigned int destinationId = netMessage.getDestinationId();
53
54 std::list<ModIdentifierEntry> neededEntries;
55 unsigned int neededLength = 0;
56
57 // Build up a list of all of the files this client has
58 std::map<std::string, ModIdentifierEntry*> hasEntries;
59 {
60 std::list<ModIdentifierEntry>::iterator hasItor;
61 for (hasItor = message.getFiles().begin();
62 hasItor != message.getFiles().end();
63 ++hasItor)
64 {
65 ModIdentifierEntry *entry = &(*hasItor);
66 hasEntries[entry->fileName] = entry;
67 }
68 }
69
70 // Build up a list of mod files that this client needs
71 // and does not already have
72 {
73 std::map<std::string, ModFileEntry *> &modFiles =
74 ScorchedServer::instance()->getModFiles().getFiles();
75 std::map<std::string, ModFileEntry *>::iterator itor;
76 for (itor = modFiles.begin();
77 itor != modFiles.end();
78 ++itor)
79 {
80 const std::string &fileName = (*itor).first;
81 ModFileEntry *fileEntry = (*itor).second;
82
83 // See if this file exists (or is correct) for this client
84 std::map<std::string, ModIdentifierEntry*>::iterator hasEntryItor =
85 hasEntries.find(fileName);
86 if (hasEntryItor == hasEntries.end() ||
87 hasEntryItor->second->crc != fileEntry->getUncompressedCrc() ||
88 hasEntryItor->second->length != fileEntry->getUncompressedSize())
89 {
90 #ifdef _DEBUG
91 if (hasEntryItor == hasEntries.end())
92 {
93 ServerCommon::serverLog(
94 S3D::formatStringBuffer("Mod download \"%s\" new file",
95 fileName.c_str()));
96 }
97 else if (hasEntryItor->second->length != fileEntry->getUncompressedSize())
98 {
99 ServerCommon::serverLog(
100 S3D::formatStringBuffer("Mod download \"%s\" size difference %u %u",
101 fileName.c_str(),
102 hasEntryItor->second->length,
103 fileEntry->getUncompressedSize()));
104 }
105 else if (hasEntryItor->second->crc != fileEntry->getUncompressedCrc())
106 {
107 ServerCommon::serverLog(
108 S3D::formatStringBuffer("Mod download \"%s\" crc difference %u %u",
109 fileName.c_str(),
110 hasEntryItor->second->crc,
111 fileEntry->getUncompressedCrc()));
112 }
113 #endif //_DEBUG
114
115 ModIdentifierEntry newEntry(true,
116 fileEntry->getFileName(),
117 0, fileEntry->getUncompressedCrc());
118 neededEntries.push_back(newEntry);
119 neededLength += fileEntry->getUncompressedSize();
120 }
121
122 // Say that this file will be sent to the client
123 if (hasEntryItor != hasEntries.end()) {
124 hasEntries.erase(hasEntryItor);
125 }
126 }
127
128 std::map<std::string, ModIdentifierEntry*>::iterator hasItor;
129 for (hasItor = hasEntries.begin();
130 hasItor != hasEntries.end();
131 ++hasItor)
132 {
133 #ifdef _DEBUG
134 ServerCommon::serverLog(
135 S3D::formatStringBuffer("Mod download \"%s\" removing file",
136 hasItor->first.c_str()));
137 #endif //_DEBUG
138
139 ModIdentifierEntry newEntry(false,
140 hasItor->first,
141 0, 0);
142 neededEntries.push_back(newEntry);
143 }
144 }
145
146 if (neededEntries.empty())
147 {
148 // No files need downloading
149 ScorchedServer::instance()->getServerChannelManager().sendText(
150 ChannelText("info",
151 "NO_MOD_FILES",
152 "No mod files need downloading"),
153 netMessage.getDestinationId(),
154 false);
155 }
156 #ifndef S3D_SERVER
157 else
158 {
159 // Do a sanity check that single player games don't need to download
160 // any mod files. As the server and client is the same process and
161 // should be using the same mod directory.
162 S3D::dialogExit("ModFiles",
163 "ERROR: Single player client required mod files");
164 }
165 #else
166 else if (ScorchedServer::instance()->getOptionsGame().getModDownloadSpeed() == 0)
167 {
168 // If this server does not allow file downloads tell the client
169 // and disconnect them
170 std::string reason = S3D::formatStringBuffer(
171 "This server requires the \"%s\" Scorched3D mod.\n"
172 "The server does not allow in game file downloads.\n"
173 "You must download and install this mod before you\n"
174 "can connect to this server.",
175 ScorchedServer::instance()->getOptionsGame().getMod());
176 ServerCommon::kickDestination(destinationId, reason);
177 }
178 #endif
179
180 // Set the files to download in this tanks profile
181 ServerDestination *destination =
182 ScorchedServer::instance()->getServerDestinations().getDestination(
183 netMessage.getDestinationId());
184 if (!destination) return false;
185
186 // and for each needed entry
187 std::list<ModIdentifierEntry>::iterator neededItor;
188 for (neededItor = neededEntries.begin();
189 neededItor != neededEntries.end();
190 ++neededItor)
191 {
192 ModIdentifierEntry &entry = (*neededItor);
193 destination->getMod().addFile(entry);
194 }
195 destination->getMod().setInit(true);
196 destination->getMod().setTotalLeft(neededLength);
197
198 return true;
199 }
200