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