Fix: Data races on cursor state in OpenGL backends
[openttd-github.git] / src / network / core / tcp_content.cpp
blob6fb3b3379429eb0091d43f85832c072a29fc807c
1 /*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
8 /**
9 * @file tcp_content.cpp Basic functions to receive and send Content packets.
12 #include "../../stdafx.h"
13 #ifndef OPENTTD_MSU
14 #include "../../textfile_gui.h"
15 #include "../../newgrf_config.h"
16 #include "../../base_media_base.h"
17 #include "../../ai/ai.hpp"
18 #include "../../game/game.hpp"
19 #include "../../fios.h"
20 #endif /* OPENTTD_MSU */
21 #include "tcp_content.h"
23 #include "../../safeguards.h"
25 /** Clear everything in the struct */
26 ContentInfo::ContentInfo()
28 memset(this, 0, sizeof(*this));
31 /** Free everything allocated */
32 ContentInfo::~ContentInfo()
34 free(this->dependencies);
35 free(this->tags);
38 /**
39 * Copy data from other #ContentInfo and take ownership of allocated stuff.
40 * @param other Source to copy from. #dependencies and #tags will be NULLed.
42 void ContentInfo::TransferFrom(ContentInfo *other)
44 if (other != this) {
45 free(this->dependencies);
46 free(this->tags);
47 memcpy(this, other, sizeof(ContentInfo));
48 other->dependencies = nullptr;
49 other->tags = nullptr;
53 /**
54 * Get the size of the data as send over the network.
55 * @return the size.
57 size_t ContentInfo::Size() const
59 size_t len = 0;
60 for (uint i = 0; i < this->tag_count; i++) len += strlen(this->tags[i]) + 1;
62 /* The size is never larger than the content info size plus the size of the
63 * tags and dependencies */
64 return sizeof(*this) +
65 sizeof(this->dependency_count) +
66 sizeof(*this->dependencies) * this->dependency_count;
69 /**
70 * Is the state either selected or autoselected?
71 * @return true iff that's the case
73 bool ContentInfo::IsSelected() const
75 switch (this->state) {
76 case ContentInfo::SELECTED:
77 case ContentInfo::AUTOSELECTED:
78 case ContentInfo::ALREADY_HERE:
79 return true;
81 default:
82 return false;
86 /**
87 * Is the information from this content info valid?
88 * @return true iff it's valid
90 bool ContentInfo::IsValid() const
92 return this->state < ContentInfo::INVALID && this->type >= CONTENT_TYPE_BEGIN && this->type < CONTENT_TYPE_END;
95 #ifndef OPENTTD_MSU
96 /**
97 * Search a textfile file next to this file in the content list.
98 * @param type The type of the textfile to search for.
99 * @return The filename for the textfile, \c nullptr otherwise.
101 const char *ContentInfo::GetTextfile(TextfileType type) const
103 if (this->state == INVALID) return nullptr;
104 const char *tmp;
105 switch (this->type) {
106 default: NOT_REACHED();
107 case CONTENT_TYPE_AI:
108 tmp = AI::GetScannerInfo()->FindMainScript(this, true);
109 break;
110 case CONTENT_TYPE_AI_LIBRARY:
111 tmp = AI::GetScannerLibrary()->FindMainScript(this, true);
112 break;
113 case CONTENT_TYPE_GAME:
114 tmp = Game::GetScannerInfo()->FindMainScript(this, true);
115 break;
116 case CONTENT_TYPE_GAME_LIBRARY:
117 tmp = Game::GetScannerLibrary()->FindMainScript(this, true);
118 break;
119 case CONTENT_TYPE_NEWGRF: {
120 const GRFConfig *gc = FindGRFConfig(BSWAP32(this->unique_id), FGCM_EXACT, this->md5sum);
121 tmp = gc != nullptr ? gc->filename : nullptr;
122 break;
124 case CONTENT_TYPE_BASE_GRAPHICS:
125 tmp = TryGetBaseSetFile(this, true, BaseGraphics::GetAvailableSets());
126 break;
127 case CONTENT_TYPE_BASE_SOUNDS:
128 tmp = TryGetBaseSetFile(this, true, BaseSounds::GetAvailableSets());
129 break;
130 case CONTENT_TYPE_BASE_MUSIC:
131 tmp = TryGetBaseSetFile(this, true, BaseMusic::GetAvailableSets());
132 break;
133 case CONTENT_TYPE_SCENARIO:
134 case CONTENT_TYPE_HEIGHTMAP:
135 extern const char *FindScenario(const ContentInfo *ci, bool md5sum);
136 tmp = FindScenario(this, true);
137 break;
139 if (tmp == nullptr) return nullptr;
140 return ::GetTextfile(type, GetContentInfoSubDir(this->type), tmp);
142 #endif /* OPENTTD_MSU */
144 void NetworkContentSocketHandler::Close()
146 CloseConnection();
147 if (this->sock == INVALID_SOCKET) return;
149 closesocket(this->sock);
150 this->sock = INVALID_SOCKET;
154 * Handle the given packet, i.e. pass it to the right
155 * parser receive command.
156 * @param p the packet to handle
157 * @return true if we should immediately handle further packets, false otherwise
159 bool NetworkContentSocketHandler::HandlePacket(Packet *p)
161 PacketContentType type = (PacketContentType)p->Recv_uint8();
163 switch (this->HasClientQuit() ? PACKET_CONTENT_END : type) {
164 case PACKET_CONTENT_CLIENT_INFO_LIST: return this->Receive_CLIENT_INFO_LIST(p);
165 case PACKET_CONTENT_CLIENT_INFO_ID: return this->Receive_CLIENT_INFO_ID(p);
166 case PACKET_CONTENT_CLIENT_INFO_EXTID: return this->Receive_CLIENT_INFO_EXTID(p);
167 case PACKET_CONTENT_CLIENT_INFO_EXTID_MD5: return this->Receive_CLIENT_INFO_EXTID_MD5(p);
168 case PACKET_CONTENT_SERVER_INFO: return this->Receive_SERVER_INFO(p);
169 case PACKET_CONTENT_CLIENT_CONTENT: return this->Receive_CLIENT_CONTENT(p);
170 case PACKET_CONTENT_SERVER_CONTENT: return this->Receive_SERVER_CONTENT(p);
172 default:
173 if (this->HasClientQuit()) {
174 DEBUG(net, 0, "[tcp/content] received invalid packet type %d from %s", type, this->client_addr.GetAddressAsString().c_str());
175 } else {
176 DEBUG(net, 0, "[tcp/content] received illegal packet from %s", this->client_addr.GetAddressAsString().c_str());
178 return false;
183 * Receive a packet at TCP level
184 * @return Whether at least one packet was received.
186 bool NetworkContentSocketHandler::ReceivePackets()
189 * We read only a few of the packets. This as receiving packets can be expensive
190 * due to the re-resolving of the parent/child relations and checking the toggle
191 * state of all bits. We cannot do this all in one go, as we want to show the
192 * user what we already received. Otherwise, it can take very long before any
193 * progress is shown to the end user that something has been received.
194 * It is also the case that we request extra content from the content server in
195 * case there is an unknown (in the content list) piece of content. These will
196 * come in after the main lists have been requested. As a result, we won't be
197 * getting everything reliably in one batch. Thus, we need to make subsequent
198 * updates in that case as well.
200 * As a result, we simple handle an arbitrary number of packets in one cycle,
201 * and let the rest be handled in subsequent cycles. These are ran, almost,
202 * immediately after this cycle so in speed it does not matter much, except
203 * that the user inferface will appear better responding.
205 * What arbitrary number to choose is the ultimate question though.
207 Packet *p;
208 static const int MAX_PACKETS_TO_RECEIVE = 42;
209 int i = MAX_PACKETS_TO_RECEIVE;
210 while (--i != 0 && (p = this->ReceivePacket()) != nullptr) {
211 bool cont = this->HandlePacket(p);
212 delete p;
213 if (!cont) return true;
216 return i != MAX_PACKETS_TO_RECEIVE - 1;
221 * Helper for logging receiving invalid packets.
222 * @param type The received packet type.
223 * @return Always false, as it's an error.
225 bool NetworkContentSocketHandler::ReceiveInvalidPacket(PacketContentType type)
227 DEBUG(net, 0, "[tcp/content] received illegal packet type %d from %s", type, this->client_addr.GetAddressAsString().c_str());
228 return false;
231 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_LIST(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_LIST); }
232 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_ID(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_ID); }
233 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_EXTID(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_EXTID); }
234 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_EXTID_MD5(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_EXTID_MD5); }
235 bool NetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_INFO); }
236 bool NetworkContentSocketHandler::Receive_CLIENT_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_CONTENT); }
237 bool NetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_CONTENT); }
239 #ifndef OPENTTD_MSU
241 * Helper to get the subdirectory a #ContentInfo is located in.
242 * @param type The type of content.
243 * @return The subdirectory the content is located in.
245 Subdirectory GetContentInfoSubDir(ContentType type)
247 switch (type) {
248 default: return NO_DIRECTORY;
249 case CONTENT_TYPE_AI: return AI_DIR;
250 case CONTENT_TYPE_AI_LIBRARY: return AI_LIBRARY_DIR;
251 case CONTENT_TYPE_GAME: return GAME_DIR;
252 case CONTENT_TYPE_GAME_LIBRARY: return GAME_LIBRARY_DIR;
253 case CONTENT_TYPE_NEWGRF: return NEWGRF_DIR;
255 case CONTENT_TYPE_BASE_GRAPHICS:
256 case CONTENT_TYPE_BASE_SOUNDS:
257 case CONTENT_TYPE_BASE_MUSIC:
258 return BASESET_DIR;
260 case CONTENT_TYPE_SCENARIO: return SCENARIO_DIR;
261 case CONTENT_TYPE_HEIGHTMAP: return HEIGHTMAP_DIR;
264 #endif /* OPENTTD_MSU */