1 /* $Id: tcp_content.cpp 25597 2013-07-13 09:26:11Z rubidium $ */
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
11 * @file tcp_content.cpp Basic functions to receive and send Content packets.
16 #include "../../stdafx.h"
18 #include "../../textfile_gui.h"
19 #include "../../newgrf_config.h"
20 #include "../../base_media_base.h"
21 #include "../../ai/ai.hpp"
22 #include "../../game/game.hpp"
23 #include "../../fios.h"
24 #endif /* OPENTTD_MSU */
25 #include "tcp_content.h"
27 #include "../../safeguards.h"
29 /** Clear everything in the struct */
30 ContentInfo::ContentInfo()
32 memset(this, 0, sizeof(*this));
35 /** Free everything allocated */
36 ContentInfo::~ContentInfo()
38 free(this->dependencies
);
43 * Copy data from other #ContentInfo and take ownership of allocated stuff.
44 * @param other Source to copy from. #dependencies and #tags will be NULLed.
46 void ContentInfo::TransferFrom(ContentInfo
*other
)
49 free(this->dependencies
);
51 memcpy(this, other
, sizeof(ContentInfo
));
52 other
->dependencies
= NULL
;
58 * Get the size of the data as send over the network.
61 size_t ContentInfo::Size() const
64 for (uint i
= 0; i
< this->tag_count
; i
++) len
+= strlen(this->tags
[i
]) + 1;
66 /* The size is never larger than the content info size plus the size of the
67 * tags and dependencies */
68 return sizeof(*this) +
69 sizeof(this->dependency_count
) +
70 sizeof(*this->dependencies
) * this->dependency_count
;
74 * Is the state either selected or autoselected?
75 * @return true iff that's the case
77 bool ContentInfo::IsSelected() const
79 switch (this->state
) {
80 case ContentInfo::SELECTED
:
81 case ContentInfo::AUTOSELECTED
:
82 case ContentInfo::ALREADY_HERE
:
91 * Is the information from this content info valid?
92 * @return true iff it's valid
94 bool ContentInfo::IsValid() const
96 return this->state
< ContentInfo::INVALID
&& this->type
>= CONTENT_TYPE_BEGIN
&& this->type
< CONTENT_TYPE_END
;
101 * Search a textfile file next to this file in the content list.
102 * @param type The type of the textfile to search for.
103 * @return The filename for the textfile, \c NULL otherwise.
105 const char *ContentInfo::GetTextfile(TextfileType type
) const
107 if (this->state
== INVALID
) return NULL
;
109 switch (this->type
) {
110 default: NOT_REACHED();
111 case CONTENT_TYPE_AI
:
112 tmp
= AI::GetScannerInfo()->FindMainScript(this, true);
114 case CONTENT_TYPE_AI_LIBRARY
:
115 tmp
= AI::GetScannerLibrary()->FindMainScript(this, true);
117 case CONTENT_TYPE_GAME
:
118 tmp
= Game::GetScannerInfo()->FindMainScript(this, true);
120 case CONTENT_TYPE_GAME_LIBRARY
:
121 tmp
= Game::GetScannerLibrary()->FindMainScript(this, true);
123 case CONTENT_TYPE_NEWGRF
: {
124 const GRFConfig
*gc
= FindGRFConfig(BSWAP32(this->unique_id
), FGCM_EXACT
, this->md5sum
);
125 tmp
= gc
!= NULL
? gc
->filename
: NULL
;
128 case CONTENT_TYPE_BASE_GRAPHICS
:
129 tmp
= TryGetBaseSetFile(this, true, BaseGraphics::GetAvailableSets());
131 case CONTENT_TYPE_BASE_SOUNDS
:
132 tmp
= TryGetBaseSetFile(this, true, BaseSounds::GetAvailableSets());
134 case CONTENT_TYPE_BASE_MUSIC
:
135 tmp
= TryGetBaseSetFile(this, true, BaseMusic::GetAvailableSets());
137 case CONTENT_TYPE_SCENARIO
:
138 case CONTENT_TYPE_HEIGHTMAP
:
139 extern const char *FindScenario(const ContentInfo
*ci
, bool md5sum
);
140 tmp
= FindScenario(this, true);
143 if (tmp
== NULL
) return NULL
;
144 return ::GetTextfile(type
, GetContentInfoSubDir(this->type
), tmp
);
146 #endif /* OPENTTD_MSU */
148 void NetworkContentSocketHandler::Close()
151 if (this->sock
== INVALID_SOCKET
) return;
153 closesocket(this->sock
);
154 this->sock
= INVALID_SOCKET
;
158 * Handle the given packet, i.e. pass it to the right
159 * parser receive command.
160 * @param p the packet to handle
161 * @return true if we should immediately handle further packets, false otherwise
163 bool NetworkContentSocketHandler::HandlePacket(Packet
*p
)
165 PacketContentType type
= (PacketContentType
)p
->Recv_uint8();
167 switch (this->HasClientQuit() ? PACKET_CONTENT_END
: type
) {
168 case PACKET_CONTENT_CLIENT_INFO_LIST
: return this->Receive_CLIENT_INFO_LIST(p
);
169 case PACKET_CONTENT_CLIENT_INFO_ID
: return this->Receive_CLIENT_INFO_ID(p
);
170 case PACKET_CONTENT_CLIENT_INFO_EXTID
: return this->Receive_CLIENT_INFO_EXTID(p
);
171 case PACKET_CONTENT_CLIENT_INFO_EXTID_MD5
: return this->Receive_CLIENT_INFO_EXTID_MD5(p
);
172 case PACKET_CONTENT_SERVER_INFO
: return this->Receive_SERVER_INFO(p
);
173 case PACKET_CONTENT_CLIENT_CONTENT
: return this->Receive_CLIENT_CONTENT(p
);
174 case PACKET_CONTENT_SERVER_CONTENT
: return this->Receive_SERVER_CONTENT(p
);
177 if (this->HasClientQuit()) {
178 DEBUG(net
, 0, "[tcp/content] received invalid packet type %d from %s", type
, this->client_addr
.GetAddressAsString());
180 DEBUG(net
, 0, "[tcp/content] received illegal packet from %s", this->client_addr
.GetAddressAsString());
187 * Receive a packet at TCP level
188 * @return Whether at least one packet was received.
190 bool NetworkContentSocketHandler::ReceivePackets()
193 * We read only a few of the packets. This as receiving packets can be expensive
194 * due to the re-resolving of the parent/child relations and checking the toggle
195 * state of all bits. We cannot do this all in one go, as we want to show the
196 * user what we already received. Otherwise, it can take very long before any
197 * progress is shown to the end user that something has been received.
198 * It is also the case that we request extra content from the content server in
199 * case there is an unknown (in the content list) piece of content. These will
200 * come in after the main lists have been requested. As a result, we won't be
201 * getting everything reliably in one batch. Thus, we need to make subsequent
202 * updates in that case as well.
204 * As a result, we simple handle an arbitrary number of packets in one cycle,
205 * and let the rest be handled in subsequent cycles. These are ran, almost,
206 * immediately after this cycle so in speed it does not matter much, except
207 * that the user inferface will appear better responding.
209 * What arbitrary number to choose is the ultimate question though.
212 static const int MAX_PACKETS_TO_RECEIVE
= 42;
213 int i
= MAX_PACKETS_TO_RECEIVE
;
214 while (--i
!= 0 && (p
= this->ReceivePacket()) != NULL
) {
215 bool cont
= this->HandlePacket(p
);
217 if (!cont
) return true;
220 return i
!= MAX_PACKETS_TO_RECEIVE
- 1;
225 * Helper for logging receiving invalid packets.
226 * @param type The received packet type.
227 * @return Always false, as it's an error.
229 bool NetworkContentSocketHandler::ReceiveInvalidPacket(PacketContentType type
)
231 DEBUG(net
, 0, "[tcp/content] received illegal packet type %d from %s", type
, this->client_addr
.GetAddressAsString());
235 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_LIST(Packet
*p
) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_LIST
); }
236 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_ID(Packet
*p
) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_ID
); }
237 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_EXTID(Packet
*p
) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_EXTID
); }
238 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_EXTID_MD5(Packet
*p
) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_EXTID_MD5
); }
239 bool NetworkContentSocketHandler::Receive_SERVER_INFO(Packet
*p
) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_INFO
); }
240 bool NetworkContentSocketHandler::Receive_CLIENT_CONTENT(Packet
*p
) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_CONTENT
); }
241 bool NetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet
*p
) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_CONTENT
); }
245 * Helper to get the subdirectory a #ContentInfo is located in.
246 * @param type The type of content.
247 * @return The subdirectory the content is located in.
249 Subdirectory
GetContentInfoSubDir(ContentType type
)
252 default: return NO_DIRECTORY
;
253 case CONTENT_TYPE_AI
: return AI_DIR
;
254 case CONTENT_TYPE_AI_LIBRARY
: return AI_LIBRARY_DIR
;
255 case CONTENT_TYPE_GAME
: return GAME_DIR
;
256 case CONTENT_TYPE_GAME_LIBRARY
: return GAME_LIBRARY_DIR
;
257 case CONTENT_TYPE_NEWGRF
: return NEWGRF_DIR
;
259 case CONTENT_TYPE_BASE_GRAPHICS
:
260 case CONTENT_TYPE_BASE_SOUNDS
:
261 case CONTENT_TYPE_BASE_MUSIC
:
264 case CONTENT_TYPE_SCENARIO
: return SCENARIO_DIR
;
265 case CONTENT_TYPE_HEIGHTMAP
: return HEIGHTMAP_DIR
;
268 #endif /* OPENTTD_MSU */
270 #endif /* ENABLE_NETWORK */