Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
RuleSave.cpp
Go to the documentation of this file.
1#include "RuleSave.hpp"
2
3#include "BoardRules.hpp"
4#include "LegacyTextIds.hpp"
5#include "Messaging.hpp"
6#include "PhaseStack.hpp"
7#include "RuleRejections.hpp"
8#include "RuleArchive.hpp"
9#include "RuleResync.hpp"
10
11#include <algorithm>
12#include <cstddef>
13#include <cstdint>
14#include <span>
15#include <utility>
16
18{
19 namespace
20 {
22
23
24 void clearAIStates()
25 {
26 for (auto& state :
28 {
29 state.clear();
30 }
31 }
32
33
34 void completed(
35 const actions::Message& message,
36 bool success)
37 {
40 NotifyActionCompleted,
43 static_cast<std::int64_t>(
44 message.action
45 ),
46 success ? 1 : 0,
47 message.fromPlayer,
48 0
49 );
50 }
51
52
54 const GameState& state,
55 const actions::Message& message)
56 {
58 }
59
60
61 void errorWrongPhase(
62 const GameState& state,
63 const actions::Message& message)
64 {
66 }
67
68
69 void sendRestart()
70 {
75 );
76 }
77
78
80 const GameState& state)
81 {
82 // Legacy checks valid undo records, including beneath nested phases.
84 }
85
86
87 bool validRiffBlob(
88 const std::vector<std::uint8_t>& data,
89 std::size_t& usefulSize)
90 {
91 usefulSize = 0;
92
93
94 if (data.size() < 8)
95 {
96 return false;
97 }
98
99
100 const std::uint32_t chunkSize =
101 static_cast<std::uint32_t>(
102 data[4]
103 ) |
104 (
105 static_cast<std::uint32_t>(
106 data[5]
107 ) << 8
108 ) |
109 (
110 static_cast<std::uint32_t>(
111 data[6]
112 ) << 16
113 ) |
114 (
115 static_cast<std::uint32_t>(
116 data[7]
117 ) << 24
118 );
119
120
121 const std::uint64_t total =
122 static_cast<std::uint64_t>(
124 ) + 8ull;
125
126
127 if (
128 total > data.size())
129 {
130 return false;
131 }
132
133
134 usefulSize =
135 static_cast<std::size_t>(
136 total
137 );
138
139
140 return true;
141 }
142 }
143
144
146 {
148 }
149
150
153 const actions::Message& message)
154 {
155 // ActionGetGameState() original.
156
157 if (
159 {
161 state,
162 message
163 );
164
165 return;
166 }
167
168
169 const GamePhase current =
171
172
173 // Une sauvegarde est déjà en cours.
174 if (
175 current ==
176 GamePhase::
178 {
180 state,
181 message
182 );
183
184 return;
185 }
186
187
188 // Phases où Restart n'est pas sûr.
189 if (
190 current ==
192 current ==
194 current ==
195 GamePhase::
197 {
199 state,
200 message
201 );
202
203 return;
204 }
205
206
207 // Seuls les vrais slots joueur peuvent recevoir
208 // le blob de sauvegarde.
209 if (
210 message.fromPlayer >=
212 {
214 state,
215 message
216 );
217
218 return;
219 }
220
221
222 // Shared temporary state non sérialisable.
223 if (
225 current == GamePhase::Auction)
226 {
228 state,
229 message
230 );
231
232 return;
233 }
234
235
236 // Equivalent moderne de StackedRulesStates[].valid.
237 if (
239 {
240 completed(
241 message,
242 false
243 );
244
245
251 0,
252 message.fromPlayer,
253 0
254 );
255
256
257 return;
258 }
259
260
262
263
264 std::uint32_t waitingForAI = 0;
265
266
267 for (PlayerNumber player = 0;
268 player <
270 ++player)
271 {
272 if (
273 state.players[player]
274 .aiPlayerLevel != 0)
275 {
276 waitingForAI |=
277 1u << player;
278 }
279 }
280
281
283 state,
284 GamePhase::
286
287 static_cast<PlayerNumber>(
289 ),
290
291 message.fromPlayer,
292
293 static_cast<std::int64_t>(
295 )
296 );
297
298
299 sendRestart();
300 }
301
302
305 const actions::Message& message)
306 {
307 // ActionAISaveParameters() original.
308
309 if (
312 GamePhase::
314 {
316 state,
317 message
318 );
319
320 return;
321 }
322
323
324 const PlayerNumber player =
325 message.fromPlayer;
326
327
328 if (
329 player >=
331 state.players[player]
332 .aiPlayerLevel == 0)
333 {
335 state,
336 message
337 );
338
339 return;
340 }
341
342
343 // Retirer l'AI du set attendu.
344 std::uint32_t waitingSet =
346 .fromPlayer;
347
348
349 waitingSet &=
350 ~(1u << player);
351
352
354 .fromPlayer =
355 static_cast<PlayerNumber>(
357 );
358
359
360 collectedAIStates[player]
361 .clear();
362
363
364 // L'ancien moteur sauvegardait les paramètres IA
365 // sous forme RIFF opaque.
366 //
367 // On conserve exactement cette propriété :
368 // RULE n'interprète pas leur contenu.
369
370 std::size_t usefulSize = 0;
371
372
373 if (
375 message.binaryDataA,
377 ))
378 {
379 collectedAIStates[player]
380 .assign(
381 message.binaryDataA.begin(),
382 message.binaryDataA.begin() +
383 static_cast<std::ptrdiff_t>(
385 )
386 );
387 }
388
389
390 // Pas de restart intermédiaire.
391 // Seulement lorsque le dernier AI a répondu.
392 if (waitingSet == 0)
393 {
394 sendRestart();
395 }
396 }
397
398
401 const actions::Message& message)
402 {
403 // ActionSetGameState() original.
404
405 if (
407 message.fromPlayer >=
409 {
411 state,
412 message
413 );
414
415 return;
416 }
417
418
420
422 testAIStates{};
423
424
425 // Premier décodage :
426 // valider avant de toucher à l'état courant.
427 if (
429 message.binaryDataA,
430 testState,
432 ))
433 {
434 completed(
435 message,
436 false
437 );
438
439
443 message.fromPlayer,
445 0,
446 message.fromPlayer,
447 0
448 );
449
450
451 return;
452 }
453
454
455 // ----------------------------------------------------
456 // Commit.
457 // ----------------------------------------------------
458
459 // Freshly decoded value state has no transient undo records. Commit
460 // clears the previous game's snapshots only after successful validation.
461 state =
462 std::move(testState);
463
464
466 std::move(testAIStates);
467
468
469 completed(
470 message,
471 true
472 );
473
474
475 // Réinitialisation des consommateurs UI.
480 0,
481 message.numberB,
482 0,
483 0
484 );
485
486
487 // ----------------------------------------------------
488 // Tous les joueurs deviennent locaux.
489 // ----------------------------------------------------
491
492 bool humanFound = false;
493
495
496
497 for (PlayerNumber player = 0;
498 player <
500 ++player)
501 {
502 if (
503 state.players[player]
504 .currentSquare < 41)
505 {
506 if (
507 state.players[player]
508 .aiPlayerLevel == 0)
509 {
510 humanFound = true;
511 }
512 else
513 {
515 player;
516 }
517 }
518
519
522 NotifyAddLocalPlayer,
525 player,
526 0,
527 0,
528 0,
529 state.players[player].name
530 );
531 }
532
533
534 // Original :
535 // si la sauvegarde ne contient plus aucun humain,
536 // forcer un AI survivant en humain.
537 if (
538 !humanFound &&
540 {
543 ].aiPlayerLevel = 0;
544 }
545
546
547 // Réémettre tout l'état observable.
549 state,
553 );
554
555
556 // Recalcul des données prédéfinies dépendant du
557 // short-game housesPerHotel.
560 );
561 }
562
563
566 const actions::Message& message)
567 {
568 // ActionGetOptionsForSave() original.
569
570 if (
571 message.fromPlayer >=
573 {
575 state,
576 message
577 );
578
579 return;
580 }
581
582
583 actions::Message result{};
584
585 result.action =
588
589 result.fromPlayer =
591
592 result.toPlayer =
593 message.fromPlayer;
594
595
596 if (
599 result.binaryDataA
600 ))
601 {
605 message.fromPlayer,
606 legacy_text::
607 ErrorSaveOptionsFailure,
608 0,
609 message.fromPlayer,
610 0
611 );
612
613
614 return;
615 }
616
617
619 result
620 );
621 }
622
623
626 const actions::Message& message)
627 {
628 // ActionResyncClient() original.
629
631 state,
632 static_cast<PlayerNumber>(
633 message.numberA
634 ),
637 );
638 }
639
640
643 {
644 if (
647 GamePhase::
649 {
650 return false;
651 }
652
653
654 const PendingPhase savePhase =
656
657
658 // ----------------------------------------------------
659 // Demander les états AI.
660 // ----------------------------------------------------
661
664 NotifyAINeedParametersForSave,
667 savePhase.fromPlayer,
668 savePhase.toPlayer,
669 0,
670 0
671 );
672
673
674 if (
675 savePhase.fromPlayer != 0)
676 {
677 return true;
678 }
679
680
681 // ----------------------------------------------------
682 // Tous les AI ont répondu.
683 //
684 // Pop AVANT sérialisation :
685 // GF_COLLECT_AI_PARAMETERS_FOR_SAVE ne doit pas être
686 // présent dans le fichier sauvegardé.
687 // ----------------------------------------------------
688
689 const PlayerNumber requester =
690 savePhase.toPlayer;
691
692
694
695 sendRestart();
696
697
698 actions::Message result{};
699
700 result.action =
703
704 result.fromPlayer =
706
707 result.toPlayer =
708 requester;
709
710
711 if (
713 state,
715 result.binaryDataA
716 ))
717 {
722 legacy_text::
723 ErrorSaveGameFailure,
724 0,
725 requester,
726 0
727 );
728
729
731
732 return true;
733 }
734
735
737 result
738 );
739
740
742
743
744 return true;
745 }
746
747
750 {
751 // ActionTick() :
752 // timeout GF_COLLECT_AI_PARAMETERS_FOR_SAVE.
753
754 if (
757 GamePhase::
759 {
760 return;
761 }
762
763
764 const PendingPhase savePhase =
766
767
768 if (
770 static_cast<std::uint64_t>(
771 savePhase.amount
772 ))
773 {
774 return;
775 }
776
777
778 if (
780 static_cast<std::uint64_t>(
781 savePhase.amount
782 ) <= 100)
783 {
784 return;
785 }
786
787
793 0,
794 savePhase.toPlayer,
795 0
796 );
797
798
800
801
803
804 sendRestart();
805 }
806}
807
std::vector< std::uint8_t > data
unsigned index
constexpr std::int64_t ErrorSaveGameLater
constexpr std::int64_t ErrorLoadGameFailure
constexpr std::int64_t ErrorSaveGameFailure
bool sendAction(const actions::Message &message)
bool decodeSave(std::span< const std::uint8_t > data, GameState &state, AIStateArray *aiStates)
bool encodeOptions(const GameOptions &options, std::vector< std::uint8_t > &result)
std::array< std::vector< std::uint8_t >, MaxPlayers > AIStateArray
bool encodeSave(const GameState &state, const AIStateArray &aiStates, std::vector< std::uint8_t > &result)
bool initializeForOptions(const GameOptions &options)
bool pop(GameState &state)
const PendingPhase & current(const GameState &state)
bool hasSnapshots(const GameState &state)
bool push(GameState &state, GamePhase phase, PlayerNumber fromPlayer, PlayerNumber toPlayer, std::int64_t amount)
void wrongPlayer(const GameState &state, const actions::Message &message)
void wrongPhase(const GameState &state, const actions::Message &message)
void sendAll(const GameState &state, PlayerNumber toPlayer, Cause cause, const archive::AIStateArray &aiStates)
void actionAISaveParameters(GameState &state, const actions::Message &message)
Definition RuleSave.cpp:303
void actionSetGameState(GameState &state, const actions::Message &message)
Definition RuleSave.cpp:399
void actionGetOptionsForSave(GameState &state, const actions::Message &message)
Definition RuleSave.cpp:564
void actionGetGameState(GameState &state, const actions::Message &message)
Definition RuleSave.cpp:151
bool restartSavePhase(GameState &state)
Definition RuleSave.cpp:641
void actionResyncClient(GameState &state, const actions::Message &message)
Definition RuleSave.cpp:624
void onIdleTick(GameState &state)
Definition RuleSave.cpp:748
constexpr PlayerNumber BankPlayer
Definition RuleTypes.hpp:15
std::uint8_t PlayerNumber
Definition RuleTypes.hpp:12
const GameState & state()
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
constexpr PlayerNumber AllPlayers
Definition RuleTypes.hpp:17
rules::PlayerNumber fromPlayer
Definition Actions.hpp:128
std::vector< std::uint8_t > binaryDataA
Definition Actions.hpp:143
std::uint64_t gameDurationInSeconds
std::array< PendingPhase, MaxPendingPhases > phaseStack
std::uint8_t numberOfPendingPhases
std::array< PlayerState, MaxPlayers > players