Zappy GUI
C++ graphic client: renderer, camera, network
Loading...
Searching...
No Matches
ProtocolParser.cpp
Go to the documentation of this file.
1#include "ProtocolParser.hpp"
2
3#include <iostream>
4#include <unordered_map>
5
6std::optional<Event> ProtocolParser::parse(std::string_view input)
7{
8 std::vector<std::string_view> tokens = _split(input, ' ');
9
10 if (tokens.empty()) return std::nullopt;
11
12 // Map command -> parser function
13 using ParserFunc = std::optional<Event> (*)(const std::vector<std::string_view>&);
14 static const std::unordered_map<std::string_view, ParserFunc> parsers = {
15 {"welcome", _parseWelcome}, {"WELCOME", _parseWelcome}, {"msz", _parseMSZ},
16 {"bct", _parseBCT}, {"tna", _parseTNA}, {"pnw", _parsePNW},
17 {"ppo", _parsePPO}, {"plv", _parsePLV}, {"pin", _parsePIN},
18 {"pex", _parsePEX}, {"pbc", _parsePBC}, {"pic", _parsePIC},
19 {"pie", _parsePIE}, {"pfk", _parsePFK}, {"pdr", _parsePDR},
20 {"pgt", _parsePGT}, {"pdi", _parsePDI}, {"enw", _parseENW},
21 {"ebo", _parseEBO}, {"edi", _parseEDI}, {"sgt", _parseSGT},
22 {"sst", _parseSST}, {"seg", _parseSEG}, {"smg", _parseSMG},
23 {"suc", _parseSUC}, {"sbp", _parseSBP}, {"stu", _parseSTU},
24 {"sse", _parseSSE}, {"gwt", _parseGWT}};
25
26 auto it = parsers.find(tokens[0]);
27 if (it != parsers.end()) {
28 return it->second(tokens);
29 }
30
31 std::cerr << "[ProtocolParser] Unknown command: " << tokens[0] << "\n";
32 return std::nullopt;
33}
34
35std::vector<std::string_view> ProtocolParser::_split(std::string_view str, char delimiter,
36 char stopAt)
37{
38 std::vector<std::string_view> tokens;
39
40 size_t stopPos = str.find(stopAt);
41 if (stopPos == std::string_view::npos) {
42 stopPos = str.size();
43 }
44
45 size_t start = 0;
46 while (start < stopPos) {
47 size_t end = str.find(delimiter, start);
48
49 if (end == std::string_view::npos || end >= stopPos) {
50 end = stopPos;
51 }
52
53 if (end > start) {
54 tokens.emplace_back(str.substr(start, end - start));
55 }
56
57 start = end + 1; // Skip delimiter
58 }
59
60 return tokens;
61}
62
63std::string ProtocolParser::_joinTokens(const std::vector<std::string_view>& tokens, size_t start)
64{
65 std::string result;
66 for (size_t i = start; i < tokens.size(); ++i) {
67 if (i > start) result += " ";
68 result += tokens[i];
69 }
70 return result;
71}
72
73std::optional<Event> ProtocolParser::_parseWelcome(
74 [[maybe_unused]] const std::vector<std::string_view>& tokens)
75{
76 // Server send "WELCOME", we do nothing
77 return std::nullopt;
78}
79
80std::optional<Event> ProtocolParser::_parseMSZ(const std::vector<std::string_view>& tokens)
81{
82 // msz X Y
83
84 if (tokens.size() != 3) return std::nullopt;
85
86 try {
87 int width = std::stoi(std::string(tokens[1]));
88 int height = std::stoi(std::string(tokens[2]));
89 return MapSize{width, height};
90 } catch (...) {
91 return std::nullopt;
92 }
93}
94
95std::optional<Event> ProtocolParser::_parseBCT(const std::vector<std::string_view>& tokens)
96{
97 // bct X Y q0 q1 q2 q3 q4 q5 q6
98
99 if (tokens.size() != 10) return std::nullopt;
100
101 try {
102 int x = std::stoi(std::string(tokens[1]));
103 int y = std::stoi(std::string(tokens[2]));
104
105 Resources resources;
106 for (size_t i = 0; i < 7; ++i) {
107 resources[i] = std::stoi(std::string(tokens[3 + i]));
108 }
109
110 return TileContent{x, y, resources};
111 } catch (...) {
112 return std::nullopt;
113 }
114}
115
116std::optional<Event> ProtocolParser::_parseTNA(const std::vector<std::string_view>& tokens)
117{
118 // tna N (N can contain spaces)
119
120 if (tokens.size() < 2) return std::nullopt;
121
122 return TeamName{_joinTokens(tokens, 1)};
123}
124
125std::optional<Event> ProtocolParser::_parsePNW(const std::vector<std::string_view>& tokens)
126{
127 // pnw #n X Y O L N
128
129 if (tokens.size() != 7) return std::nullopt;
130
131 try {
132 int id = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
133 int x = std::stoi(std::string(tokens[2]));
134 int y = std::stoi(std::string(tokens[3]));
135
136 // Orientation is 1-based in the protocol, and our enum is also 1-based, so we can directly
137 // cast
138 Orientation orientation = static_cast<Orientation>(std::stoi(std::string(tokens[4])));
139 int level = std::stoi(std::string(tokens[5]));
140 std::string team = std::string(tokens[6]);
141
142 return PlayerNew{id, x, y, orientation, level, team};
143 } catch (...) {
144 return std::nullopt;
145 }
146}
147
148std::optional<Event> ProtocolParser::_parsePPO(const std::vector<std::string_view>& tokens)
149{
150 // ppo #n X Y O
151
152 if (tokens.size() != 5) return std::nullopt;
153
154 try {
155 int id = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
156 int x = std::stoi(std::string(tokens[2]));
157 int y = std::stoi(std::string(tokens[3]));
158
159 // Orientation is 1-based in the protocol, and our enum is also 1-based, so we can directly
160 // cast
161 Orientation orientation = static_cast<Orientation>(std::stoi(std::string(tokens[4])));
162
163 return PlayerPosition{id, x, y, orientation};
164 } catch (...) {
165 return std::nullopt;
166 }
167}
168
169std::optional<Event> ProtocolParser::_parsePLV(const std::vector<std::string_view>& tokens)
170{
171 // plv #n L
172
173 if (tokens.size() != 3) return std::nullopt;
174
175 try {
176 int id = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
177 int level = std::stoi(std::string(tokens[2]));
178
179 return PlayerLevel{id, level};
180 } catch (...) {
181 return std::nullopt;
182 }
183}
184
185std::optional<Event> ProtocolParser::_parsePIN(const std::vector<std::string_view>& tokens)
186{
187 // pin #n X Y q0 q1 q2 q3 q4 q5 q6
188
189 if (tokens.size() != 11) return std::nullopt;
190
191 try {
192 int id = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
193 int x = std::stoi(std::string(tokens[2]));
194 int y = std::stoi(std::string(tokens[3]));
195
196 Resources inventory;
197 for (size_t i = 0; i < 7; ++i) {
198 inventory[i] = std::stoi(std::string(tokens[4 + i]));
199 }
200
201 return PlayerInventory{id, x, y, inventory};
202 } catch (...) {
203 return std::nullopt;
204 }
205}
206
207std::optional<Event> ProtocolParser::_parsePEX(const std::vector<std::string_view>& tokens)
208{
209 // pex #n
210
211 if (tokens.size() != 2) return std::nullopt;
212
213 try {
214 int id = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
215 return PlayerExpulsion{id};
216 } catch (...) {
217 return std::nullopt;
218 }
219}
220
221std::optional<Event> ProtocolParser::_parsePBC(const std::vector<std::string_view>& tokens)
222{
223 // pbc #n M (M can contain spaces)
224
225 if (tokens.size() < 3) return std::nullopt;
226
227 try {
228 int id = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
229 return PlayerBroadcast{id, _joinTokens(tokens, 2)};
230 } catch (...) {
231 return std::nullopt;
232 }
233}
234
235std::optional<Event> ProtocolParser::_parsePIC(const std::vector<std::string_view>& tokens)
236{
237 // pic X Y L #n1 #n2 ...
238
239 if (tokens.size() < 5) return std::nullopt;
240
241 try {
242 int x = std::stoi(std::string(tokens[1]));
243 int y = std::stoi(std::string(tokens[2]));
244 int level = std::stoi(std::string(tokens[3]));
245
246 std::vector<int> playerIds;
247 for (size_t i = 4; i < tokens.size(); ++i) {
248 playerIds.push_back(std::stoi(std::string(tokens[i].substr(1)))); // Skip '#'
249 }
250
251 return IncantationStart{x, y, level, playerIds};
252 } catch (...) {
253 return std::nullopt;
254 }
255}
256
257std::optional<Event> ProtocolParser::_parsePIE(const std::vector<std::string_view>& tokens)
258{
259 // pie X Y R
260
261 if (tokens.size() != 4) return std::nullopt;
262
263 try {
264 int x = std::stoi(std::string(tokens[1]));
265 int y = std::stoi(std::string(tokens[2]));
266
267 // R is 1 for success, 0 for failure
268 bool success = (tokens[3] == "1");
269
270 return IncantationEnd{x, y, success};
271 } catch (...) {
272 return std::nullopt;
273 }
274}
275
276std::optional<Event> ProtocolParser::_parsePFK(const std::vector<std::string_view>& tokens)
277{
278 // pfk #n
279
280 if (tokens.size() != 2) return std::nullopt;
281
282 try {
283 int id = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
284 return PlayerFork{id};
285 } catch (...) {
286 return std::nullopt;
287 }
288}
289
290std::optional<Event> ProtocolParser::_parsePDR(const std::vector<std::string_view>& tokens)
291{
292 // pdr #n i
293
294 if (tokens.size() != 3) return std::nullopt;
295
296 try {
297 int playerId = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
298 int resourceId = std::stoi(std::string(tokens[2]));
299 return PlayerResourceDrop{playerId, resourceId};
300 } catch (...) {
301 return std::nullopt;
302 }
303}
304
305std::optional<Event> ProtocolParser::_parsePGT(const std::vector<std::string_view>& tokens)
306{
307 // pgt #n i
308
309 if (tokens.size() != 3) return std::nullopt;
310
311 try {
312 int playerId = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
313 int resourceId = std::stoi(std::string(tokens[2]));
314 return PlayerResourceTake{playerId, resourceId};
315 } catch (...) {
316 return std::nullopt;
317 }
318}
319
320std::optional<Event> ProtocolParser::_parsePDI(const std::vector<std::string_view>& tokens)
321{
322 // pdi #n
323
324 if (tokens.size() != 2) return std::nullopt;
325
326 try {
327 int id = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
328 return PlayerDeath{id};
329 } catch (...) {
330 return std::nullopt;
331 }
332}
333
334std::optional<Event> ProtocolParser::_parseENW(const std::vector<std::string_view>& tokens)
335{
336 // enw #e #n X Y
337
338 if (tokens.size() != 5) return std::nullopt;
339
340 try {
341 int eggId = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
342 int playerId = std::stoi(std::string(tokens[2].substr(1))); // Skip '#'
343 int x = std::stoi(std::string(tokens[3]));
344 int y = std::stoi(std::string(tokens[4]));
345
346 return EggNew{eggId, playerId, x, y};
347 } catch (...) {
348 return std::nullopt;
349 }
350}
351
352std::optional<Event> ProtocolParser::_parseEBO(const std::vector<std::string_view>& tokens)
353{
354 // ebo #e
355
356 if (tokens.size() != 2) return std::nullopt;
357
358 try {
359 int id = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
360 return EggHatch{id};
361 } catch (...) {
362 return std::nullopt;
363 }
364}
365
366std::optional<Event> ProtocolParser::_parseEDI(const std::vector<std::string_view>& tokens)
367{
368 // edi #e
369
370 if (tokens.size() != 2) return std::nullopt;
371
372 try {
373 int id = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
374 return EggDeath{id};
375 } catch (...) {
376 return std::nullopt;
377 }
378}
379
380std::optional<Event> ProtocolParser::_parseSGT(const std::vector<std::string_view>& tokens)
381{
382 // sgt T
383
384 if (tokens.size() != 2) return std::nullopt;
385
386 try {
387 int timeUnit = std::stoi(std::string(tokens[1]));
388 return TimeUnit{timeUnit};
389 } catch (...) {
390 return std::nullopt;
391 }
392}
393
394std::optional<Event> ProtocolParser::_parseSST(const std::vector<std::string_view>& tokens)
395{
396 // sst T
397
398 if (tokens.size() != 2) return std::nullopt;
399
400 try {
401 int timeUnit = std::stoi(std::string(tokens[1]));
402 return TimeUnitChange{timeUnit};
403 } catch (...) {
404 return std::nullopt;
405 }
406}
407
408std::optional<Event> ProtocolParser::_parseSEG(const std::vector<std::string_view>& tokens)
409{
410 // seg N (N can contain spaces)
411
412 if (tokens.size() < 2) return std::nullopt;
413
414 return GameEnd{_joinTokens(tokens, 1)};
415}
416
417std::optional<Event> ProtocolParser::_parseSMG(const std::vector<std::string_view>& tokens)
418{
419 // smg M (M can contain spaces)
420
421 if (tokens.size() < 2) return std::nullopt;
422
423 return ServerMessage{_joinTokens(tokens, 1)};
424}
425
426std::optional<Event> ProtocolParser::_parseSUC(const std::vector<std::string_view>& tokens)
427{
428 // suc
429
430 if (tokens.size() != 1) return std::nullopt;
431
432 return UnknownCommand{};
433}
434
435std::optional<Event> ProtocolParser::_parseSBP(const std::vector<std::string_view>& tokens)
436{
437 // sbp
438
439 if (tokens.size() != 1) return std::nullopt;
440
441 return BadParameters{};
442}
443
444std::optional<Event> ProtocolParser::_parseSTU(const std::vector<std::string_view>& tokens)
445{
446 // stu T U
447
448 if (tokens.size() != 3) return std::nullopt;
449
450 try {
451 int uptimeSeconds = std::stoi(std::string(tokens[1]));
452 int tickCount = std::stoi(std::string(tokens[2]));
453 return ServerUptime{uptimeSeconds, tickCount};
454 } catch (...) {
455 return std::nullopt;
456 }
457}
458
459std::optional<Event> ProtocolParser::_parseSSE(const std::vector<std::string_view>& tokens)
460{
461 // sse #e N X Y
462
463 if (tokens.size() != 5) return std::nullopt;
464
465 try {
466 int eggId = std::stoi(std::string(tokens[1].substr(1))); // Skip '#'
467 std::string team = std::string(tokens[2]);
468 int x = std::stoi(std::string(tokens[3]));
469 int y = std::stoi(std::string(tokens[4]));
470
471 return ServerSpawnedEgg{eggId, team, x, y};
472 } catch (...) {
473 return std::nullopt;
474 }
475}
476
477std::optional<Event> ProtocolParser::_parseGWT(const std::vector<std::string_view>& tokens)
478{
479 // gwt <team> <seconds> <ticks>
480 if (tokens.size() != 4) return std::nullopt;
481
482 try {
483 std::string team = std::string(tokens[1]);
484 int seconds = std::stoi(std::string(tokens[2]));
485 int64_t ticks = std::stoll(std::string(tokens[3]));
486 return WinDuration{team, seconds, ticks};
487 } catch (...) {
488 return std::nullopt;
489 }
490}
Orientation
static std::optional< Event > _parseSTU(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePFK(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseSGT(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseSBP(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePPO(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePNW(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseGWT(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseEBO(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePLV(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseSEG(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePIE(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseWelcome(const std::vector< std::string_view > &tokens)
static std::optional< Event > parse(std::string_view input)
Parses a raw input string from the server and converts it into an Event variant. This method stops at...
static std::optional< Event > _parsePEX(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseSUC(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePBC(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseSST(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseMSZ(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePIN(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseSMG(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePDI(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseEDI(const std::vector< std::string_view > &tokens)
static std::vector< std::string_view > _split(std::string_view str, char delimiter, char stopAt='\n')
static std::string _joinTokens(const std::vector< std::string_view > &tokens, size_t start)
static std::optional< Event > _parseTNA(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseSSE(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseBCT(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePGT(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePIC(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parseENW(const std::vector< std::string_view > &tokens)
static std::optional< Event > _parsePDR(const std::vector< std::string_view > &tokens)