Maintain a circular buffer of recent commands, add to crashlog.
[openttd-joker.git] / src / network / core / tcp_content.cpp
blob79bc39064f88334d5eaf7a96084ef62a4970ec51
1 /* $Id: tcp_content.cpp 25597 2013-07-13 09:26:11Z rubidium $ */
3 /*
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/>.
8 */
10 /**
11 * @file tcp_content.cpp Basic functions to receive and send Content packets.
14 #ifdef ENABLE_NETWORK
16 #include "../../stdafx.h"
17 #ifndef OPENTTD_MSU
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);
39 free(this->tags);
42 /**
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)
48 if (other != this) {
49 free(this->dependencies);
50 free(this->tags);
51 memcpy(this, other, sizeof(ContentInfo));
52 other->dependencies = NULL;
53 other->tags = NULL;
57 /**
58 * Get the size of the data as send over the network.
59 * @return the size.
61 size_t ContentInfo::Size() const
63 size_t len = 0;
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;
73 /**
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:
83 return true;
85 default:
86 return false;
90 /**
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;
99 #ifndef OPENTTD_MSU
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;
108 const char *tmp;
109 switch (this->type) {
110 default: NOT_REACHED();
111 case CONTENT_TYPE_AI:
112 tmp = AI::GetScannerInfo()->FindMainScript(this, true);
113 break;
114 case CONTENT_TYPE_AI_LIBRARY:
115 tmp = AI::GetScannerLibrary()->FindMainScript(this, true);
116 break;
117 case CONTENT_TYPE_GAME:
118 tmp = Game::GetScannerInfo()->FindMainScript(this, true);
119 break;
120 case CONTENT_TYPE_GAME_LIBRARY:
121 tmp = Game::GetScannerLibrary()->FindMainScript(this, true);
122 break;
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;
126 break;
128 case CONTENT_TYPE_BASE_GRAPHICS:
129 tmp = TryGetBaseSetFile(this, true, BaseGraphics::GetAvailableSets());
130 break;
131 case CONTENT_TYPE_BASE_SOUNDS:
132 tmp = TryGetBaseSetFile(this, true, BaseSounds::GetAvailableSets());
133 break;
134 case CONTENT_TYPE_BASE_MUSIC:
135 tmp = TryGetBaseSetFile(this, true, BaseMusic::GetAvailableSets());
136 break;
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);
141 break;
143 if (tmp == NULL) return NULL;
144 return ::GetTextfile(type, GetContentInfoSubDir(this->type), tmp);
146 #endif /* OPENTTD_MSU */
148 void NetworkContentSocketHandler::Close()
150 CloseConnection();
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);
176 default:
177 if (this->HasClientQuit()) {
178 DEBUG(net, 0, "[tcp/content] received invalid packet type %d from %s", type, this->client_addr.GetAddressAsString());
179 } else {
180 DEBUG(net, 0, "[tcp/content] received illegal packet from %s", this->client_addr.GetAddressAsString());
182 return false;
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.
211 Packet *p;
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);
216 delete 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());
232 return false;
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); }
243 #ifndef OPENTTD_MSU
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)
251 switch (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:
262 return BASESET_DIR;
264 case CONTENT_TYPE_SCENARIO: return SCENARIO_DIR;
265 case CONTENT_TYPE_HEIGHTMAP: return HEIGHTMAP_DIR;
268 #endif /* OPENTTD_MSU */
270 #endif /* ENABLE_NETWORK */