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 */
7
8 /**
9 * @file tcp_content.cpp Basic functions to receive and send Content packets.
10 */
11
12 #include "../../stdafx.h"
13 #include "../../textfile_gui.h"
14 #include "../../newgrf_config.h"
15 #include "../../base_media_base.h"
16 #include "../../ai/ai.hpp"
17 #include "../../game/game.hpp"
18 #include "../../fios.h"
19 #include "tcp_content.h"
20
21 #include "../../safeguards.h"
22
23 /**
24 * Is the state either selected or autoselected?
25 * @return true iff that's the case
26 */
IsSelected() const27 bool ContentInfo::IsSelected() const
28 {
29 switch (this->state) {
30 case ContentInfo::SELECTED:
31 case ContentInfo::AUTOSELECTED:
32 case ContentInfo::ALREADY_HERE:
33 return true;
34
35 default:
36 return false;
37 }
38 }
39
40 /**
41 * Is the information from this content info valid?
42 * @return true iff it's valid
43 */
IsValid() const44 bool ContentInfo::IsValid() const
45 {
46 return this->state < ContentInfo::INVALID && this->type >= CONTENT_TYPE_BEGIN && this->type < CONTENT_TYPE_END;
47 }
48
49 /**
50 * Search a textfile file next to this file in the content list.
51 * @param type The type of the textfile to search for.
52 * @return The filename for the textfile, \c nullptr otherwise.
53 */
GetTextfile(TextfileType type) const54 const char *ContentInfo::GetTextfile(TextfileType type) const
55 {
56 if (this->state == INVALID) return nullptr;
57 const char *tmp;
58 switch (this->type) {
59 default: NOT_REACHED();
60 case CONTENT_TYPE_AI:
61 tmp = AI::GetScannerInfo()->FindMainScript(this, true);
62 break;
63 case CONTENT_TYPE_AI_LIBRARY:
64 tmp = AI::GetScannerLibrary()->FindMainScript(this, true);
65 break;
66 case CONTENT_TYPE_GAME:
67 tmp = Game::GetScannerInfo()->FindMainScript(this, true);
68 break;
69 case CONTENT_TYPE_GAME_LIBRARY:
70 tmp = Game::GetScannerLibrary()->FindMainScript(this, true);
71 break;
72 case CONTENT_TYPE_NEWGRF: {
73 const GRFConfig *gc = FindGRFConfig(BSWAP32(this->unique_id), FGCM_EXACT, this->md5sum);
74 tmp = gc != nullptr ? gc->filename : nullptr;
75 break;
76 }
77 case CONTENT_TYPE_BASE_GRAPHICS:
78 tmp = TryGetBaseSetFile(this, true, BaseGraphics::GetAvailableSets());
79 break;
80 case CONTENT_TYPE_BASE_SOUNDS:
81 tmp = TryGetBaseSetFile(this, true, BaseSounds::GetAvailableSets());
82 break;
83 case CONTENT_TYPE_BASE_MUSIC:
84 tmp = TryGetBaseSetFile(this, true, BaseMusic::GetAvailableSets());
85 break;
86 case CONTENT_TYPE_SCENARIO:
87 case CONTENT_TYPE_HEIGHTMAP:
88 extern const char *FindScenario(const ContentInfo *ci, bool md5sum);
89 tmp = FindScenario(this, true);
90 break;
91 }
92 if (tmp == nullptr) return nullptr;
93 return ::GetTextfile(type, GetContentInfoSubDir(this->type), tmp);
94 }
95
96 /**
97 * Handle the given packet, i.e. pass it to the right
98 * parser receive command.
99 * @param p the packet to handle
100 * @return true if we should immediately handle further packets, false otherwise
101 */
HandlePacket(Packet * p)102 bool NetworkContentSocketHandler::HandlePacket(Packet *p)
103 {
104 PacketContentType type = (PacketContentType)p->Recv_uint8();
105
106 switch (this->HasClientQuit() ? PACKET_CONTENT_END : type) {
107 case PACKET_CONTENT_CLIENT_INFO_LIST: return this->Receive_CLIENT_INFO_LIST(p);
108 case PACKET_CONTENT_CLIENT_INFO_ID: return this->Receive_CLIENT_INFO_ID(p);
109 case PACKET_CONTENT_CLIENT_INFO_EXTID: return this->Receive_CLIENT_INFO_EXTID(p);
110 case PACKET_CONTENT_CLIENT_INFO_EXTID_MD5: return this->Receive_CLIENT_INFO_EXTID_MD5(p);
111 case PACKET_CONTENT_SERVER_INFO: return this->Receive_SERVER_INFO(p);
112 case PACKET_CONTENT_CLIENT_CONTENT: return this->Receive_CLIENT_CONTENT(p);
113 case PACKET_CONTENT_SERVER_CONTENT: return this->Receive_SERVER_CONTENT(p);
114
115 default:
116 if (this->HasClientQuit()) {
117 Debug(net, 0, "[tcp/content] Received invalid packet type {}", type);
118 } else {
119 Debug(net, 0, "[tcp/content] Received illegal packet");
120 }
121 return false;
122 }
123 }
124
125 /**
126 * Receive a packet at TCP level
127 * @return Whether at least one packet was received.
128 */
ReceivePackets()129 bool NetworkContentSocketHandler::ReceivePackets()
130 {
131 /*
132 * We read only a few of the packets. This as receiving packets can be expensive
133 * due to the re-resolving of the parent/child relations and checking the toggle
134 * state of all bits. We cannot do this all in one go, as we want to show the
135 * user what we already received. Otherwise, it can take very long before any
136 * progress is shown to the end user that something has been received.
137 * It is also the case that we request extra content from the content server in
138 * case there is an unknown (in the content list) piece of content. These will
139 * come in after the main lists have been requested. As a result, we won't be
140 * getting everything reliably in one batch. Thus, we need to make subsequent
141 * updates in that case as well.
142 *
143 * As a result, we simple handle an arbitrary number of packets in one cycle,
144 * and let the rest be handled in subsequent cycles. These are ran, almost,
145 * immediately after this cycle so in speed it does not matter much, except
146 * that the user inferface will appear better responding.
147 *
148 * What arbitrary number to choose is the ultimate question though.
149 */
150 Packet *p;
151 static const int MAX_PACKETS_TO_RECEIVE = 42;
152 int i = MAX_PACKETS_TO_RECEIVE;
153 while (--i != 0 && (p = this->ReceivePacket()) != nullptr) {
154 bool cont = this->HandlePacket(p);
155 delete p;
156 if (!cont) return true;
157 }
158
159 return i != MAX_PACKETS_TO_RECEIVE - 1;
160 }
161
162
163 /**
164 * Helper for logging receiving invalid packets.
165 * @param type The received packet type.
166 * @return Always false, as it's an error.
167 */
ReceiveInvalidPacket(PacketContentType type)168 bool NetworkContentSocketHandler::ReceiveInvalidPacket(PacketContentType type)
169 {
170 Debug(net, 0, "[tcp/content] Received illegal packet type {}", type);
171 return false;
172 }
173
Receive_CLIENT_INFO_LIST(Packet * p)174 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_LIST(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_LIST); }
Receive_CLIENT_INFO_ID(Packet * p)175 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_ID(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_ID); }
Receive_CLIENT_INFO_EXTID(Packet * p)176 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_EXTID(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_EXTID); }
Receive_CLIENT_INFO_EXTID_MD5(Packet * p)177 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_EXTID_MD5(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_EXTID_MD5); }
Receive_SERVER_INFO(Packet * p)178 bool NetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_INFO); }
Receive_CLIENT_CONTENT(Packet * p)179 bool NetworkContentSocketHandler::Receive_CLIENT_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_CONTENT); }
Receive_SERVER_CONTENT(Packet * p)180 bool NetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_CONTENT); }
181
182 /**
183 * Helper to get the subdirectory a #ContentInfo is located in.
184 * @param type The type of content.
185 * @return The subdirectory the content is located in.
186 */
GetContentInfoSubDir(ContentType type)187 Subdirectory GetContentInfoSubDir(ContentType type)
188 {
189 switch (type) {
190 default: return NO_DIRECTORY;
191 case CONTENT_TYPE_AI: return AI_DIR;
192 case CONTENT_TYPE_AI_LIBRARY: return AI_LIBRARY_DIR;
193 case CONTENT_TYPE_GAME: return GAME_DIR;
194 case CONTENT_TYPE_GAME_LIBRARY: return GAME_LIBRARY_DIR;
195 case CONTENT_TYPE_NEWGRF: return NEWGRF_DIR;
196
197 case CONTENT_TYPE_BASE_GRAPHICS:
198 case CONTENT_TYPE_BASE_SOUNDS:
199 case CONTENT_TYPE_BASE_MUSIC:
200 return BASESET_DIR;
201
202 case CONTENT_TYPE_SCENARIO: return SCENARIO_DIR;
203 case CONTENT_TYPE_HEIGHTMAP: return HEIGHTMAP_DIR;
204 }
205 }
206