Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
RuleArchive.cpp
Go to the documentation of this file.
1#include "RuleArchive.hpp"
2#include "RuleOptions.hpp"
3
4#include <algorithm>
5#include <array>
6#include <cstddef>
7#include <cstdint>
8#include <limits>
9#include <string>
10#include <type_traits>
11#include <utility>
12#include <vector>
13
15{
16 namespace
17 {
18 constexpr std::array<std::uint8_t, 8>
20 'M', 'O', 'N', 'O',
21 '9', '9', 'S', 'V'
22 };
23
24 constexpr std::array<std::uint8_t, 8>
26 'M', 'O', 'N', 'O',
27 '9', '9', 'O', 'P'
28 };
29
30 constexpr std::uint32_t FormatVersion = 2;
31
32
33 class Writer
34 {
35 public:
36 void u8(std::uint8_t value)
37 {
38 data_.push_back(value);
39 }
40
41
42 void u32(std::uint32_t value)
43 {
44 for (int shift = 0;
45 shift < 32;
46 shift += 8)
47 {
48 u8(
49 static_cast<std::uint8_t>(
50 value >> shift
51 )
52 );
53 }
54 }
55
56
57 void u64(std::uint64_t value)
58 {
59 for (int shift = 0;
60 shift < 64;
61 shift += 8)
62 {
63 u8(
64 static_cast<std::uint8_t>(
65 value >> shift
66 )
67 );
68 }
69 }
70
71
72 void i64(std::int64_t value)
73 {
74 u64(
75 static_cast<std::uint64_t>(
76 value
77 )
78 );
79 }
80
81
82 void boolean(bool value)
83 {
84 u8(value ? 1 : 0);
85 }
86
87
88 void raw(
89 std::span<const std::uint8_t> data)
90 {
91 data_.insert(
92 data_.end(),
93 data.begin(),
94 data.end()
95 );
96 }
97
98
99 void blob(
100 const std::vector<std::uint8_t>& data)
101 {
102 u32(
103 static_cast<std::uint32_t>(
104 data.size()
105 )
106 );
107
108 raw(data);
109 }
110
111
112 void text(
113 const std::wstring& value)
114 {
115 u32(
116 static_cast<std::uint32_t>(
117 value.size()
118 )
119 );
120
121 // On enregistre les unités wchar_t sous forme
122 // d'entiers 32 bits.
123 //
124 // Cela évite de dépendre de la taille du wchar_t
125 // ou d'une page de codes Windows.
126
127 for (wchar_t ch : value)
128 {
129 u32(
130 static_cast<std::uint32_t>(
131 ch
132 )
133 );
134 }
135 }
136
137
138 const std::vector<std::uint8_t>& data() const
139 {
140 return data_;
141 }
142
143
144 std::vector<std::uint8_t> take()
145 {
146 return std::move(data_);
147 }
148
149 private:
150 std::vector<std::uint8_t> data_;
151 };
152
153
154 class Reader
155 {
156 public:
157 explicit Reader(
158 std::span<const std::uint8_t> data)
159 : data_(data)
160 {
161 }
162
163
164 bool u8(std::uint8_t& value)
165 {
166 if (position_ >= data_.size())
167 {
168 return false;
169 }
170
171 value =
172 data_[position_++];
173
174 return true;
175 }
176
177
178 bool u32(std::uint32_t& value)
179 {
180 value = 0;
181
182 for (int shift = 0;
183 shift < 32;
184 shift += 8)
185 {
186 std::uint8_t byte = 0;
187
188 if (!u8(byte))
189 {
190 return false;
191 }
192
193 value |=
194 static_cast<std::uint32_t>(
195 byte
196 ) << shift;
197 }
198
199 return true;
200 }
201
202
203 bool u64(std::uint64_t& value)
204 {
205 value = 0;
206
207 for (int shift = 0;
208 shift < 64;
209 shift += 8)
210 {
211 std::uint8_t byte = 0;
212
213 if (!u8(byte))
214 {
215 return false;
216 }
217
218 value |=
219 static_cast<std::uint64_t>(
220 byte
221 ) << shift;
222 }
223
224 return true;
225 }
226
227
228 bool i64(std::int64_t& value)
229 {
230 std::uint64_t rawValue = 0;
231
232 if (!u64(rawValue))
233 {
234 return false;
235 }
236
237 value =
238 static_cast<std::int64_t>(
239 rawValue
240 );
241
242 return true;
243 }
244
245
246 bool boolean(bool& value)
247 {
248 std::uint8_t rawValue = 0;
249
250 if (!u8(rawValue) ||
251 rawValue > 1)
252 {
253 return false;
254 }
255
256 value =
257 rawValue != 0;
258
259 return true;
260 }
261
262
263 bool raw(
264 std::size_t size,
265 std::vector<std::uint8_t>& result)
266 {
267 if (
268 size >
269 data_.size() - position_)
270 {
271 return false;
272 }
273
274 result.assign(
275 data_.begin() +
276 static_cast<std::ptrdiff_t>(
277 position_
278 ),
279
280 data_.begin() +
281 static_cast<std::ptrdiff_t>(
282 position_ + size
283 )
284 );
285
286 position_ += size;
287
288 return true;
289 }
290
291
292 bool blob(
293 std::vector<std::uint8_t>& result)
294 {
295 std::uint32_t size = 0;
296
297 if (!u32(size))
298 {
299 return false;
300 }
301
302 return raw(
303 size,
304 result
305 );
306 }
307
308
309 bool text(
310 std::wstring& result)
311 {
312 std::uint32_t size = 0;
313
314 if (!u32(size) ||
315 size > 4096)
316 {
317 return false;
318 }
319
320 result.clear();
321 result.reserve(size);
322
323 for (std::uint32_t i = 0;
324 i < size;
325 ++i)
326 {
327 std::uint32_t ch = 0;
328
329 if (!u32(ch))
330 {
331 return false;
332 }
333
334 result.push_back(
335 static_cast<wchar_t>(
336 ch
337 )
338 );
339 }
340
341 return true;
342 }
343
344
345 bool finished() const
346 {
347 return
348 position_ ==
349 data_.size();
350 }
351
352 private:
353 std::span<const std::uint8_t>
354 data_;
355
356 std::size_t position_ = 0;
357 };
358
359
360 template<typename T>
361 void writeNumber(
362 Writer& writer,
363 T value)
364 {
365 writer.i64(
366 static_cast<std::int64_t>(
367 value
368 )
369 );
370 }
371
372
373 template<typename T>
374 bool readNumber(
375 Reader& reader,
376 T& value)
377 {
378 std::int64_t rawValue = 0;
379
380 if (!reader.i64(rawValue))
381 {
382 return false;
383 }
384
385 value =
386 static_cast<T>(
387 rawValue
388 );
389
390 return true;
391 }
392
393
394 std::uint64_t fnv1a(
395 std::span<const std::uint8_t> data)
396 {
397 std::uint64_t hash =
398 14695981039346656037ull;
399
400 for (const std::uint8_t byte :
401 data)
402 {
403 hash ^= byte;
404
405 hash *=
406 1099511628211ull;
407 }
408
409 return hash;
410 }
411
412
414 Writer& writer,
415 const GameOptions& options)
416 {
417#define WRITE_OPTION(field) \
418 writeNumber(writer, options.field)
419
420#define WRITE_BOOL_OPTION(field) \
421 writer.boolean(options.field)
422
423 WRITE_OPTION(housesPerHotel);
424 WRITE_OPTION(maximumHouses);
425 WRITE_OPTION(maximumHotels);
426
427 WRITE_OPTION(interestRate);
428 WRITE_OPTION(initialCash);
429 WRITE_OPTION(passingGoAmount);
430 WRITE_OPTION(luxuryTaxAmount);
431 WRITE_OPTION(taxRate);
432 WRITE_OPTION(flatTaxFee);
433
434 WRITE_OPTION(freeParkingSeed);
435
436 WRITE_BOOL_OPTION(freeParkingPot);
437 WRITE_BOOL_OPTION(doubleSalaryOnGo);
438 WRITE_BOOL_OPTION(hideCash);
439 WRITE_BOOL_OPTION(evenBuildRule);
440
442 rollDiceToDecideStartingOrder
443 );
444
445 WRITE_BOOL_OPTION(cheatingAllowed);
446
448 aiTakesTimeToThink
449 );
450
452 futureRentTradingAllowed
453 );
454
456 immunitiesTradingAllowed
457 );
458
460 allowPlayersToTakeOverAIs
461 );
462
464 dealFreePropertiesAtStartup
465 );
466
468 dealNPropertiesAtStartup
469 );
470
472 stopAtNthBankruptcy
473 );
474
476 maximumTurnsInJail
477 );
478
480 getOutOfJailFee
481 );
482
484 mortgagedCountsInGroupRent
485 );
486
488 houseShortageLevel
489 );
490
492 hotelShortageLevel
493 );
494
496 auctionGoingTimeDelay
497 );
498
500 inactivityWarningTime
501 );
502
504 gameOverTimeLimit
505 );
506
508 writer,
509 options.voiceChat.recordingHz
510 );
511
513 writer,
515 );
516
517 writer.text(
519 );
520
521#undef WRITE_OPTION
522#undef WRITE_BOOL_OPTION
523 }
524
525
527 Reader& reader,
528 GameOptions& options)
529 {
530#define READ_OPTION(field) \
531 if (!readNumber(reader, options.field)) return false
532
533#define READ_BOOL_OPTION(field) \
534 if (!reader.boolean(options.field)) return false
535
536 READ_OPTION(housesPerHotel);
537 READ_OPTION(maximumHouses);
538 READ_OPTION(maximumHotels);
539
540 READ_OPTION(interestRate);
541 READ_OPTION(initialCash);
542 READ_OPTION(passingGoAmount);
543 READ_OPTION(luxuryTaxAmount);
544 READ_OPTION(taxRate);
545 READ_OPTION(flatTaxFee);
546
547 READ_OPTION(freeParkingSeed);
548
549 READ_BOOL_OPTION(freeParkingPot);
550 READ_BOOL_OPTION(doubleSalaryOnGo);
551 READ_BOOL_OPTION(hideCash);
552 READ_BOOL_OPTION(evenBuildRule);
553
555 rollDiceToDecideStartingOrder
556 );
557
558 READ_BOOL_OPTION(cheatingAllowed);
559
561 aiTakesTimeToThink
562 );
563
565 futureRentTradingAllowed
566 );
567
569 immunitiesTradingAllowed
570 );
571
573 allowPlayersToTakeOverAIs
574 );
575
577 dealFreePropertiesAtStartup
578 );
579
581 dealNPropertiesAtStartup
582 );
583
585 stopAtNthBankruptcy
586 );
587
589 maximumTurnsInJail
590 );
591
593 getOutOfJailFee
594 );
595
597 mortgagedCountsInGroupRent
598 );
599
601 houseShortageLevel
602 );
603
605 hotelShortageLevel
606 );
607
609 auctionGoingTimeDelay
610 );
611
613 inactivityWarningTime
614 );
615
617 gameOverTimeLimit
618 );
619
620 if (
621 !readNumber(
622 reader,
623 options.voiceChat.recordingHz
624 ) ||
625 !readNumber(
626 reader,
628 ) ||
629 !reader.text(
631 ))
632 {
633 return false;
634 }
635
636#undef READ_OPTION
637#undef READ_BOOL_OPTION
638
639 return true;
640 }
641
642
644 Writer& writer,
645 const GameState& state)
646 {
648 writer,
650 );
651
652
653 // ------------------------------------------------
654 // Identité de la partie.
655 // ------------------------------------------------
656
658 writer,
660 );
661
663 writer,
665 );
666
667
668 // ------------------------------------------------
669 // Joueurs.
670 // ------------------------------------------------
671
672 for (const PlayerState& player :
674 {
675 writer.text(player.name);
676
678 writer,
679 player.token
680 );
681
683 writer,
684 player.colour
685 );
686
688 writer,
689 player.aiPlayerLevel
690 );
691
693 writer,
694 player.cash
695 );
696
698 writer,
699 player.timeOfLastActivity
700 );
701
703 writer,
704 player.currentSquare
705 );
706
707 writer.boolean(
708 player.firstMoveMade
709 );
710
712 writer,
713 player.turnsInJail
714 );
715
717 writer,
718 player.inactivityCount
719 );
720
721 writer.boolean(
722 player.acceptedConfiguration
723 );
724
726 writer,
727 player.diceRollHistory
728 );
729
730 for (const auto amount :
731 player.cashGivenInTrade)
732 {
734 writer,
735 amount
736 );
737 }
738
739 writer.boolean(
740 player.tradeAccepted
741 );
742 }
743
744
745 // ------------------------------------------------
746 // Plateau.
747 // ------------------------------------------------
748
749 for (const SquareState& square :
751 {
753 writer,
754 square.owner
755 );
756
758 writer,
759 square.offeredInTradeTo
760 );
761
763 writer,
764 square.houses
765 );
766
767 writer.boolean(
768 square.mortgaged
769 );
770
772 writer,
773 square.gameEarnings
774 );
775 }
776
777
778 // ------------------------------------------------
779 // Decks.
780 // ------------------------------------------------
781
782 for (const CardDeck& deck :
783 state.cards)
784 {
786 writer,
787 deck.cardCount
788 );
789
790 for (const auto card :
791 deck.cardPile)
792 {
794 writer,
795 card
796 );
797 }
798
800 writer,
801 deck.jailOwner
802 );
803
805 writer,
806 deck.jailOfferedInTradeTo
807 );
808 }
809
810
811 // ------------------------------------------------
812 // Dés / carte pending.
813 // ------------------------------------------------
814
815 for (const auto die :
816 state.dice)
817 {
819 }
820
821 for (const auto die :
823 {
825 }
826
827 for (const auto die :
829 {
831 }
832
834 writer,
836 );
837
838 writer.boolean(
840 );
841
843 writer,
845 );
846
847
848 // ------------------------------------------------
849 // Horloge / jackpot.
850 // ------------------------------------------------
851
853 writer,
855 );
856
858 writer,
860 );
861
863 writer,
865 );
866
867
868 // ------------------------------------------------
869 // Auction shared state.
870 // ------------------------------------------------
871
873 writer,
875 );
876
878 writer,
880 );
881
883 writer,
885 );
886
888 writer,
890 );
891
893 writer,
896 );
897
898
899 // ------------------------------------------------
900 // Trade + futures / immunités.
901 // ------------------------------------------------
902
903 writer.boolean(
905 );
906
907 for (const CountHitRecord& hit :
909 {
911 writer,
912 hit.properties
913 );
914
916 writer,
917 hit.fromPlayer
918 );
919
921 writer,
922 hit.toPlayer
923 );
924
926 writer,
927 hit.hitType
928 );
929
930 writer.boolean(
931 hit.tradedItem
932 );
933
935 writer,
936 hit.hitCount
937 );
938 }
939
940
941 // ------------------------------------------------
942 // Phase stack.
943 // ------------------------------------------------
944
946 writer,
948 );
949
950 for (const PendingPhase& phase :
952 {
954 writer,
955 phase.phase
956 );
957
959 writer,
960 phase.fromPlayer
961 );
962
964 writer,
965 phase.toPlayer
966 );
967
969 writer,
970 phase.amount
971 );
972 }
973 }
974
975
976 bool readStatePayload(
977 Reader& reader,
979 {
980 if (
982 reader,
984 ))
985 {
986 return false;
987 }
988
989
990 if (
991 !readNumber(
992 reader,
994 ) ||
997 {
998 return false;
999 }
1000
1001
1002 if (
1003 !readNumber(
1004 reader,
1006 ))
1007 {
1008 return false;
1009 }
1010
1011
1012 for (PlayerState& player :
1013 state.players)
1014 {
1015 if (!reader.text(player.name))
1016 {
1017 return false;
1018 }
1019
1020 if (
1021 !readNumber(reader, player.token) ||
1022 !readNumber(reader, player.colour) ||
1023 !readNumber(
1024 reader,
1025 player.aiPlayerLevel
1026 ) ||
1027 !readNumber(reader, player.cash) ||
1028 !readNumber(
1029 reader,
1030 player.timeOfLastActivity
1031 ) ||
1032 !readNumber(
1033 reader,
1034 player.currentSquare
1035 ) ||
1036 !reader.boolean(
1037 player.firstMoveMade
1038 ) ||
1039 !readNumber(
1040 reader,
1041 player.turnsInJail
1042 ) ||
1043 !readNumber(
1044 reader,
1045 player.inactivityCount
1046 ) ||
1047 !reader.boolean(
1048 player.acceptedConfiguration
1049 ) ||
1050 !readNumber(
1051 reader,
1052 player.diceRollHistory
1053 ))
1054 {
1055 return false;
1056 }
1057
1058
1059 for (auto& amount :
1060 player.cashGivenInTrade)
1061 {
1062 if (
1063 !readNumber(
1064 reader,
1065 amount
1066 ))
1067 {
1068 return false;
1069 }
1070 }
1071
1072
1073 if (
1074 !reader.boolean(
1075 player.tradeAccepted
1076 ))
1077 {
1078 return false;
1079 }
1080 }
1081
1082
1083 for (SquareState& square :
1084 state.squares)
1085 {
1086 if (
1087 !readNumber(
1088 reader,
1089 square.owner
1090 ) ||
1091 !readNumber(
1092 reader,
1093 square.offeredInTradeTo
1094 ) ||
1095 !readNumber(
1096 reader,
1097 square.houses
1098 ) ||
1099 !reader.boolean(
1100 square.mortgaged
1101 ) ||
1102 !readNumber(
1103 reader,
1104 square.gameEarnings
1105 ))
1106 {
1107 return false;
1108 }
1109 }
1110
1111
1112 for (CardDeck& deck :
1113 state.cards)
1114 {
1115 if (
1116 !readNumber(
1117 reader,
1118 deck.cardCount
1119 ) ||
1120 deck.cardCount >
1122 {
1123 return false;
1124 }
1125
1126
1127 for (auto& card :
1128 deck.cardPile)
1129 {
1130 if (
1131 !readNumber(
1132 reader,
1133 card
1134 ))
1135 {
1136 return false;
1137 }
1138 }
1139
1140
1141 if (
1142 !readNumber(
1143 reader,
1144 deck.jailOwner
1145 ) ||
1146 !readNumber(
1147 reader,
1148 deck.jailOfferedInTradeTo
1149 ))
1150 {
1151 return false;
1152 }
1153 }
1154
1155
1156 for (auto& die : state.dice)
1157 {
1158 if (!readNumber(reader, die))
1159 {
1160 return false;
1161 }
1162 }
1163
1164 for (auto& die : state.nextDice)
1165 {
1166 if (!readNumber(reader, die))
1167 {
1168 return false;
1169 }
1170 }
1171
1172 for (auto& die :
1174 {
1175 if (!readNumber(reader, die))
1176 {
1177 return false;
1178 }
1179 }
1180
1181
1182 if (
1183 !readNumber(
1184 reader,
1186 ) ||
1187 !reader.boolean(
1189 ) ||
1190 !readNumber(
1191 reader,
1193 ) ||
1194 !readNumber(
1195 reader,
1197 ) ||
1198 !readNumber(
1199 reader,
1201 ) ||
1202 !readNumber(
1203 reader,
1205 ))
1206 {
1207 return false;
1208 }
1209
1210
1211 if (
1212 !readNumber(
1213 reader,
1215 ) ||
1216 !readNumber(
1217 reader,
1219 ) ||
1220 !readNumber(
1221 reader,
1223 ) ||
1224 !readNumber(
1225 reader,
1227 ) ||
1228 !readNumber(
1229 reader,
1232 ))
1233 {
1234 return false;
1235 }
1236
1237
1238 if (
1239 !reader.boolean(
1241 ))
1242 {
1243 return false;
1244 }
1245
1246
1247 for (CountHitRecord& hit :
1249 {
1250 if (
1251 !readNumber(
1252 reader,
1253 hit.properties
1254 ) ||
1255 !readNumber(
1256 reader,
1257 hit.fromPlayer
1258 ) ||
1259 !readNumber(
1260 reader,
1261 hit.toPlayer
1262 ) ||
1263 !readNumber(
1264 reader,
1265 hit.hitType
1266 ) ||
1267 !reader.boolean(
1268 hit.tradedItem
1269 ) ||
1270 !readNumber(
1271 reader,
1272 hit.hitCount
1273 ))
1274 {
1275 return false;
1276 }
1277 }
1278
1279
1280 if (
1281 !readNumber(
1282 reader,
1284 ) ||
1286 state.phaseStack.size())
1287 {
1288 return false;
1289 }
1290
1291
1292 for (PendingPhase& phase :
1294 {
1295 if (
1296 !readNumber(
1297 reader,
1298 phase.phase
1299 ) ||
1300 !readNumber(
1301 reader,
1302 phase.fromPlayer
1303 ) ||
1304 !readNumber(
1305 reader,
1306 phase.toPlayer
1307 ) ||
1308 !readNumber(
1309 reader,
1310 phase.amount
1311 ))
1312 {
1313 return false;
1314 }
1315 }
1316
1317
1318 // ------------------------------------------------
1319 // Validation minimale.
1320 // ------------------------------------------------
1321
1322 for (PlayerNumber player = 0;
1323 player < state.numberOfPlayers;
1324 ++player)
1325 {
1326 if (
1327 state.players[player]
1328 .currentSquare > 41)
1329 {
1330 return false;
1331 }
1332 }
1333
1334
1335 return true;
1336 }
1337
1338
1339 bool makeEnvelope(
1340 const std::array<std::uint8_t, 8>& magic,
1341 std::span<const std::uint8_t> payload,
1342 std::vector<std::uint8_t>& result)
1343 {
1344 if (
1345 payload.size() >
1346 std::numeric_limits<
1347 std::uint32_t
1348 >::max())
1349 {
1350 return false;
1351 }
1352
1353
1354 Writer writer;
1355
1356 writer.raw(magic);
1357
1358 writer.u32(
1360 );
1361
1362 writer.u32(
1363 static_cast<std::uint32_t>(
1364 payload.size()
1365 )
1366 );
1367
1368 writer.u64(
1369 fnv1a(payload)
1370 );
1371
1372 writer.raw(payload);
1373
1374
1375 result =
1376 writer.take();
1377
1378 return true;
1379 }
1380
1381
1382 bool openEnvelope(
1383 std::span<const std::uint8_t> data,
1384 const std::array<std::uint8_t, 8>& magic,
1385 std::vector<std::uint8_t>& payload)
1386 {
1387 constexpr std::size_t HeaderSize =
1388 8 + 4 + 4 + 8;
1389
1390
1391 if (data.size() < HeaderSize)
1392 {
1393 return false;
1394 }
1395
1396
1397 for (std::size_t i = 0;
1398 i < magic.size();
1399 ++i)
1400 {
1401 if (data[i] != magic[i])
1402 {
1403 return false;
1404 }
1405 }
1406
1407
1408 Reader reader(
1409 data.subspan(8)
1410 );
1411
1412
1413 std::uint32_t version = 0;
1414 std::uint32_t size = 0;
1415 std::uint64_t checksum = 0;
1416
1417
1418 if (
1419 !reader.u32(version) ||
1420 !reader.u32(size) ||
1421 !reader.u64(checksum) ||
1422 version != FormatVersion)
1423 {
1424 return false;
1425 }
1426
1427
1428 if (
1429 size !=
1430 data.size() - HeaderSize)
1431 {
1432 return false;
1433 }
1434
1435
1436 payload.assign(
1437 data.begin() +
1438 static_cast<std::ptrdiff_t>(
1440 ),
1441 data.end()
1442 );
1443
1444
1445 return
1446 fnv1a(payload) ==
1447 checksum;
1448 }
1449 }
1450
1451
1453 const GameState& state,
1454 const AIStateArray& aiStates,
1455 std::vector<std::uint8_t>& result)
1456 {
1457 Writer payload;
1458
1460 payload,
1461 state
1462 );
1463
1464
1465 for (const auto& aiState :
1466 aiStates)
1467 {
1468 payload.blob(aiState);
1469 }
1470
1471
1472 return makeEnvelope(
1473 SaveMagic,
1474 payload.data(),
1475 result
1476 );
1477 }
1478
1479
1481 std::span<const std::uint8_t> data,
1484 {
1485 std::vector<std::uint8_t> payload;
1486
1487 if (
1488 !openEnvelope(
1489 data,
1490 SaveMagic,
1491 payload
1492 ))
1493 {
1494 return false;
1495 }
1496
1497
1498 Reader reader(payload);
1499
1501
1502
1503 if (
1505 reader,
1507 ))
1508 {
1509 return false;
1510 }
1511
1512
1514 decodedState.options
1515 );
1516
1517
1519
1520
1521 for (auto& aiState :
1523 {
1524 if (!reader.blob(aiState))
1525 {
1526 return false;
1527 }
1528 }
1529
1530
1531 if (!reader.finished())
1532 {
1533 return false;
1534 }
1535
1536
1537 state =
1538 std::move(decodedState);
1539
1540
1541 if (aiStates != nullptr)
1542 {
1543 *aiStates =
1544 std::move(decodedAIStates);
1545 }
1546
1547
1548 return true;
1549 }
1550
1551
1553 const GameOptions& options,
1554 std::vector<std::uint8_t>& result)
1555 {
1556 Writer payload;
1557
1559 payload,
1560 options
1561 );
1562
1563
1564 return makeEnvelope(
1566 payload.data(),
1567 result
1568 );
1569 }
1570
1572 std::span<const std::uint8_t> data,
1573 GameOptions& result)
1574 {
1575 std::vector<std::uint8_t>
1576 payload;
1577
1578
1579 if (
1580 !openEnvelope(
1581 data,
1583 payload
1584 ))
1585 {
1586 return false;
1587 }
1588
1589
1590 Reader reader(payload);
1591
1593
1594
1595 if (
1597 reader,
1598 decoded
1599 ) ||
1600 !reader.finished())
1601 {
1602 return false;
1603 }
1604
1605
1607 decoded
1608 );
1609
1610
1611 result =
1612 std::move(decoded);
1613
1614
1615 return true;
1616 }
1617}
1618
std::vector< std::uint8_t > data
std::string_view text
unsigned index
#define WRITE_BOOL_OPTION(field)
#define READ_BOOL_OPTION(field)
#define WRITE_OPTION(field)
#define READ_OPTION(field)
std::atomic< bool > finished
std::uint16_t value
Definition Timers.cpp:16
bool decodeSave(std::span< const std::uint8_t > data, GameState &state, AIStateArray *aiStates)
bool encodeOptions(const GameOptions &options, std::vector< std::uint8_t > &result)
bool decodeOptions(std::span< const std::uint8_t > data, GameOptions &result)
std::array< std::vector< std::uint8_t >, MaxPlayers > AIStateArray
bool encodeSave(const GameState &state, const AIStateArray &aiStates, std::vector< std::uint8_t > &result)
void validate(GameOptions &options)
std::uint8_t PlayerNumber
Definition RuleTypes.hpp:12
constexpr std::size_t MaxCardsInDeck
Definition CardTypes.hpp:54
const GameState & state()
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
std::array< std::uint8_t, 2 > dice
std::array< CardDeck, static_cast< std::size_t >(DeckType::Count) > cards
std::uint8_t numberOfDoublesRolled
std::array< CountHitRecord, MaxCountHitSets > countHits
PlayerNumber configurationProposer
std::int64_t freeParkingJackpotAmount
std::array< SquareState, SquareCount > squares
std::array< std::uint8_t, 2 > utilityDice
std::uint64_t gameDurationInSeconds
std::array< PendingPhase, MaxPendingPhases > phaseStack
std::array< std::uint8_t, 2 > nextDice
std::uint8_t numberOfPendingPhases
std::array< PlayerState, MaxPlayers > players