1 #include "server_player.h"
2 
3 #include "color.h"
4 #include "decklist.h"
5 #include "get_pb_extension.h"
6 #include "pb/command_attach_card.pb.h"
7 #include "pb/command_change_zone_properties.pb.h"
8 #include "pb/command_concede.pb.h"
9 #include "pb/command_create_arrow.pb.h"
10 #include "pb/command_create_counter.pb.h"
11 #include "pb/command_create_token.pb.h"
12 #include "pb/command_deck_select.pb.h"
13 #include "pb/command_del_counter.pb.h"
14 #include "pb/command_delete_arrow.pb.h"
15 #include "pb/command_draw_cards.pb.h"
16 #include "pb/command_dump_zone.pb.h"
17 #include "pb/command_flip_card.pb.h"
18 #include "pb/command_game_say.pb.h"
19 #include "pb/command_inc_card_counter.pb.h"
20 #include "pb/command_inc_counter.pb.h"
21 #include "pb/command_kick_from_game.pb.h"
22 #include "pb/command_leave_game.pb.h"
23 #include "pb/command_move_card.pb.h"
24 #include "pb/command_mulligan.pb.h"
25 #include "pb/command_next_turn.pb.h"
26 #include "pb/command_ready_start.pb.h"
27 #include "pb/command_reveal_cards.pb.h"
28 #include "pb/command_reverse_turn.pb.h"
29 #include "pb/command_roll_die.pb.h"
30 #include "pb/command_set_active_phase.pb.h"
31 #include "pb/command_set_card_attr.pb.h"
32 #include "pb/command_set_card_counter.pb.h"
33 #include "pb/command_set_counter.pb.h"
34 #include "pb/command_set_sideboard_lock.pb.h"
35 #include "pb/command_set_sideboard_plan.pb.h"
36 #include "pb/command_shuffle.pb.h"
37 #include "pb/command_stop_dump_zone.pb.h"
38 #include "pb/command_undo_draw.pb.h"
39 #include "pb/context_concede.pb.h"
40 #include "pb/context_connection_state_changed.pb.h"
41 #include "pb/context_deck_select.pb.h"
42 #include "pb/context_move_card.pb.h"
43 #include "pb/context_mulligan.pb.h"
44 #include "pb/context_ready_start.pb.h"
45 #include "pb/context_set_sideboard_lock.pb.h"
46 #include "pb/context_undo_draw.pb.h"
47 #include "pb/event_attach_card.pb.h"
48 #include "pb/event_change_zone_properties.pb.h"
49 #include "pb/event_create_arrow.pb.h"
50 #include "pb/event_create_counter.pb.h"
51 #include "pb/event_create_token.pb.h"
52 #include "pb/event_del_counter.pb.h"
53 #include "pb/event_delete_arrow.pb.h"
54 #include "pb/event_destroy_card.pb.h"
55 #include "pb/event_draw_cards.pb.h"
56 #include "pb/event_dump_zone.pb.h"
57 #include "pb/event_flip_card.pb.h"
58 #include "pb/event_game_say.pb.h"
59 #include "pb/event_move_card.pb.h"
60 #include "pb/event_player_properties_changed.pb.h"
61 #include "pb/event_reveal_cards.pb.h"
62 #include "pb/event_reverse_turn.pb.h"
63 #include "pb/event_roll_die.pb.h"
64 #include "pb/event_set_card_attr.pb.h"
65 #include "pb/event_set_card_counter.pb.h"
66 #include "pb/event_set_counter.pb.h"
67 #include "pb/event_shuffle.pb.h"
68 #include "pb/event_stop_dump_zone.pb.h"
69 #include "pb/response.pb.h"
70 #include "pb/response_deck_download.pb.h"
71 #include "pb/response_dump_zone.pb.h"
72 #include "pb/serverinfo_player.pb.h"
73 #include "pb/serverinfo_user.pb.h"
74 #include "rng_abstract.h"
75 #include "server.h"
76 #include "server_abstractuserinterface.h"
77 #include "server_arrow.h"
78 #include "server_card.h"
79 #include "server_cardzone.h"
80 #include "server_counter.h"
81 #include "server_database_interface.h"
82 #include "server_game.h"
83 #include "server_room.h"
84 
85 #include <QDebug>
86 #include <algorithm>
87 
Server_Player(Server_Game * _game,int _playerId,const ServerInfo_User & _userInfo,bool _spectator,bool _judge,Server_AbstractUserInterface * _userInterface)88 Server_Player::Server_Player(Server_Game *_game,
89                              int _playerId,
90                              const ServerInfo_User &_userInfo,
91                              bool _spectator,
92                              bool _judge,
93                              Server_AbstractUserInterface *_userInterface)
94     : ServerInfo_User_Container(_userInfo), game(_game), userInterface(_userInterface), deck(nullptr), pingTime(0),
95       playerId(_playerId), spectator(_spectator), judge(_judge), nextCardId(0), readyStart(false), conceded(false),
96       sideboardLocked(true)
97 {
98 }
99 
100 Server_Player::~Server_Player() = default;
101 
prepareDestroy()102 void Server_Player::prepareDestroy()
103 {
104     delete deck;
105 
106     playerMutex.lock();
107     if (userInterface) {
108         userInterface->playerRemovedFromGame(game);
109     }
110     playerMutex.unlock();
111 
112     clearZones();
113 
114     deleteLater();
115 }
116 
newCardId()117 int Server_Player::newCardId()
118 {
119     return nextCardId++;
120 }
121 
newCounterId() const122 int Server_Player::newCounterId() const
123 {
124     int id = 0;
125     QMapIterator<int, Server_Counter *> i(counters);
126     while (i.hasNext()) {
127         Server_Counter *c = i.next().value();
128         if (c->getId() > id) {
129             id = c->getId();
130         }
131     }
132     return id + 1;
133 }
134 
newArrowId() const135 int Server_Player::newArrowId() const
136 {
137     int id = 0;
138     for (Server_Arrow *a : arrows) {
139         if (a->getId() > id) {
140             id = a->getId();
141         }
142     }
143     return id + 1;
144 }
145 
setupZones()146 void Server_Player::setupZones()
147 {
148     // This may need to be customized according to the game rules.
149     // ------------------------------------------------------------------
150 
151     // Create zones
152     auto *deckZone = new Server_CardZone(this, "deck", false, ServerInfo_Zone::HiddenZone);
153     addZone(deckZone);
154     auto *sbZone = new Server_CardZone(this, "sb", false, ServerInfo_Zone::HiddenZone);
155     addZone(sbZone);
156     addZone(new Server_CardZone(this, "table", true, ServerInfo_Zone::PublicZone));
157     addZone(new Server_CardZone(this, "hand", false, ServerInfo_Zone::PrivateZone));
158     addZone(new Server_CardZone(this, "stack", false, ServerInfo_Zone::PublicZone));
159     addZone(new Server_CardZone(this, "grave", false, ServerInfo_Zone::PublicZone));
160     addZone(new Server_CardZone(this, "rfg", false, ServerInfo_Zone::PublicZone));
161 
162     addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, 20));
163     addCounter(new Server_Counter(1, "w", makeColor(255, 255, 150), 20, 0));
164     addCounter(new Server_Counter(2, "u", makeColor(150, 150, 255), 20, 0));
165     addCounter(new Server_Counter(3, "b", makeColor(150, 150, 150), 20, 0));
166     addCounter(new Server_Counter(4, "r", makeColor(250, 150, 150), 20, 0));
167     addCounter(new Server_Counter(5, "g", makeColor(150, 255, 150), 20, 0));
168     addCounter(new Server_Counter(6, "x", makeColor(255, 255, 255), 20, 0));
169     addCounter(new Server_Counter(7, "storm", makeColor(255, 150, 30), 20, 0));
170 
171     // ------------------------------------------------------------------
172 
173     // Assign card ids and create deck from deck list
174     InnerDecklistNode *listRoot = deck->getRoot();
175     nextCardId = 0;
176     for (int i = 0; i < listRoot->size(); ++i) {
177         auto *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
178         Server_CardZone *z;
179         if (currentZone->getName() == DECK_ZONE_MAIN) {
180             z = deckZone;
181         } else if (currentZone->getName() == DECK_ZONE_SIDE) {
182             z = sbZone;
183         } else {
184             continue;
185         }
186 
187         for (int j = 0; j < currentZone->size(); ++j) {
188             auto *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
189             if (!currentCard) {
190                 continue;
191             }
192             for (int k = 0; k < currentCard->getNumber(); ++k) {
193                 z->insertCard(new Server_Card(currentCard->getName(), nextCardId++, 0, 0, z), -1, 0);
194             }
195         }
196     }
197 
198     const QList<MoveCard_ToZone> &sideboardPlan = deck->getCurrentSideboardPlan();
199     for (const auto &m : sideboardPlan) {
200         const QString startZone = QString::fromStdString(m.start_zone());
201         const QString targetZone = QString::fromStdString(m.target_zone());
202 
203         Server_CardZone *start, *target;
204         if (startZone == DECK_ZONE_MAIN) {
205             start = deckZone;
206         } else if (startZone == DECK_ZONE_SIDE) {
207             start = sbZone;
208         } else {
209             continue;
210         }
211         if (targetZone == DECK_ZONE_MAIN) {
212             target = deckZone;
213         } else if (targetZone == DECK_ZONE_SIDE) {
214             target = sbZone;
215         } else {
216             continue;
217         }
218 
219         for (int j = 0; j < start->getCards().size(); ++j) {
220             if (start->getCards()[j]->getName() == QString::fromStdString(m.card_name())) {
221                 Server_Card *card = start->getCard(j, nullptr, true);
222                 target->insertCard(card, -1, 0);
223                 break;
224             }
225         }
226     }
227 
228     deckZone->shuffle();
229 }
230 
clearZones()231 void Server_Player::clearZones()
232 {
233     for (Server_CardZone *zone : zones) {
234         delete zone;
235     }
236     zones.clear();
237 
238     for (Server_Counter *counter : counters) {
239         delete counter;
240     }
241     counters.clear();
242 
243     for (Server_Arrow *arrow : arrows) {
244         delete arrow;
245     }
246     arrows.clear();
247 
248     lastDrawList.clear();
249 }
250 
getProperties(ServerInfo_PlayerProperties & result,bool withUserInfo)251 void Server_Player::getProperties(ServerInfo_PlayerProperties &result, bool withUserInfo)
252 {
253     result.set_player_id(playerId);
254     if (withUserInfo) {
255         copyUserInfo(*(result.mutable_user_info()), true);
256     }
257     result.set_spectator(spectator);
258     if (!spectator) {
259         result.set_conceded(conceded);
260         result.set_sideboard_locked(sideboardLocked);
261         result.set_ready_start(readyStart);
262     }
263     result.set_judge(judge);
264     if (deck) {
265         result.set_deck_hash(deck->getDeckHash().toStdString());
266     }
267     result.set_ping_seconds(pingTime);
268 }
269 
addZone(Server_CardZone * zone)270 void Server_Player::addZone(Server_CardZone *zone)
271 {
272     zones.insert(zone->getName(), zone);
273 }
274 
addArrow(Server_Arrow * arrow)275 void Server_Player::addArrow(Server_Arrow *arrow)
276 {
277     arrows.insert(arrow->getId(), arrow);
278 }
279 
deleteArrow(int arrowId)280 bool Server_Player::deleteArrow(int arrowId)
281 {
282     Server_Arrow *arrow = arrows.value(arrowId, 0);
283     if (!arrow) {
284         return false;
285     }
286     arrows.remove(arrowId);
287     delete arrow;
288     return true;
289 }
290 
addCounter(Server_Counter * counter)291 void Server_Player::addCounter(Server_Counter *counter)
292 {
293     counters.insert(counter->getId(), counter);
294 }
295 
drawCards(GameEventStorage & ges,int number)296 Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int number)
297 {
298     Server_CardZone *deckZone = zones.value("deck");
299     Server_CardZone *handZone = zones.value("hand");
300     if (deckZone->getCards().size() < number) {
301         number = deckZone->getCards().size();
302     }
303 
304     Event_DrawCards eventOthers;
305     eventOthers.set_number(number);
306     Event_DrawCards eventPrivate(eventOthers);
307 
308     for (int i = 0; i < number; ++i) {
309         Server_Card *card = deckZone->getCard(0, nullptr, true);
310         handZone->insertCard(card, -1, 0);
311         lastDrawList.append(card->getId());
312 
313         ServerInfo_Card *cardInfo = eventPrivate.add_cards();
314         cardInfo->set_id(card->getId());
315         cardInfo->set_name(card->getName().toStdString());
316     }
317 
318     ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
319     ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
320 
321     if (deckZone->getAlwaysRevealTopCard() && !deckZone->getCards().isEmpty()) {
322         Event_RevealCards revealEvent;
323         revealEvent.set_zone_name(deckZone->getName().toStdString());
324         revealEvent.set_card_id(0);
325         deckZone->getCards().first()->getInfo(revealEvent.add_cards());
326 
327         ges.enqueueGameEvent(revealEvent, playerId);
328     }
329 
330     return Response::RespOk;
331 }
332 
333 class Server_Player::MoveCardCompareFunctor
334 {
335 private:
336     int x;
337 
338 public:
MoveCardCompareFunctor(int _x)339     explicit MoveCardCompareFunctor(int _x) : x(_x)
340     {
341     }
operator ()(QPair<Server_Card *,int> a,QPair<Server_Card *,int> b)342     inline bool operator()(QPair<Server_Card *, int> a, QPair<Server_Card *, int> b)
343     {
344         if (a.second < x) {
345             if (b.second >= x) {
346                 return false;
347             } else {
348                 return (a.second > b.second);
349             }
350         } else {
351             if (b.second < x) {
352                 return true;
353             } else {
354                 return (a.second < b.second);
355             }
356         }
357     }
358 };
359 
moveCard(GameEventStorage & ges,Server_CardZone * startzone,const QList<const CardToMove * > & _cards,Server_CardZone * targetzone,int x,int y,bool fixFreeSpaces,bool undoingDraw)360 Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges,
361                                                Server_CardZone *startzone,
362                                                const QList<const CardToMove *> &_cards,
363                                                Server_CardZone *targetzone,
364                                                int x,
365                                                int y,
366                                                bool fixFreeSpaces,
367                                                bool undoingDraw)
368 {
369     // Disallow controller change to other zones than the table.
370     if (((targetzone->getType() != ServerInfo_Zone::PublicZone) || !targetzone->hasCoords()) &&
371         (startzone->getPlayer() != targetzone->getPlayer()) && !judge) {
372         return Response::RespContextError;
373     }
374 
375     if (!targetzone->hasCoords() && (x <= -1)) {
376         x = targetzone->getCards().size();
377     }
378 
379     QList<QPair<Server_Card *, int>> cardsToMove;
380     QMap<Server_Card *, const CardToMove *> cardProperties;
381     QSet<int> cardIdsToMove;
382     for (auto _card : _cards) {
383         // The same card being moved twice would lead to undefined behaviour.
384         if (cardIdsToMove.contains(_card->card_id())) {
385             continue;
386         }
387         cardIdsToMove.insert(_card->card_id());
388 
389         // Consistency checks. In case the command contains illegal moves, try to resolve the legal ones still.
390         int position;
391         Server_Card *card = startzone->getCard(_card->card_id(), &position);
392         if (!card) {
393             return Response::RespNameNotFound;
394         }
395         if (card->getParentCard()) {
396             continue;
397         }
398         if (!card->getAttachedCards().isEmpty() && !targetzone->isColumnEmpty(x, y)) {
399             continue;
400         }
401         cardsToMove.append(QPair<Server_Card *, int>(card, position));
402         cardProperties.insert(card, _card);
403     }
404     // In case all moves were filtered out, abort.
405     if (cardsToMove.isEmpty()) {
406         return Response::RespContextError;
407     }
408 
409     // 0 performs no sorting
410     // 1 reverses the sorting
411     MoveCardCompareFunctor cmp(0);
412     std::sort(cardsToMove.begin(), cardsToMove.end(), cmp);
413 
414     bool secondHalf = false;
415     int xIndex = -1;
416 
417     for (int cardIndex = cardsToMove.size() - 1; cardIndex > -1; --cardIndex) {
418         Server_Card *card = cardsToMove[cardIndex].first;
419         const CardToMove *thisCardProperties = cardProperties.value(card);
420         bool faceDown = thisCardProperties->has_face_down() ? thisCardProperties->face_down() : card->getFaceDown();
421         if (!targetzone->hasCoords()) {
422             faceDown = false;
423         }
424 
425         int originalPosition = cardsToMove[cardIndex].second;
426         int position = startzone->removeCard(card);
427 
428         // "Undo draw" should only remain valid if the just-drawn card stays within the user's hand (e.g., they only
429         // reorder their hand). If a just-drawn card leaves the hand then clear lastDrawList to invalidate "undo draw."
430         // (Ignore the case where the card is currently being un-drawn.)
431         if (startzone->getName() == "hand" && targetzone->getName() != "hand" && lastDrawList.contains(card->getId()) &&
432             !undoingDraw) {
433             lastDrawList.clear();
434         }
435 
436         // Only mess about with x coordinates if the card is sticking around, otherwise it can have weird side effects
437         // if multiple cards are moved at once.
438         if (!card->getDestroyOnZoneChange()) {
439             if ((startzone == targetzone) && !startzone->hasCoords()) {
440                 if (!secondHalf && (originalPosition < x)) {
441                     xIndex = -1;
442                     secondHalf = true;
443                 } else if (secondHalf) {
444                     --xIndex;
445                 } else {
446                     ++xIndex;
447                 }
448             } else {
449                 ++xIndex;
450             }
451         }
452 
453         int newX = x + xIndex;
454 
455         // Attachment relationships can be retained when moving a card onto the opponent's table
456         if (startzone->getName() != targetzone->getName()) {
457             // Delete all attachment relationships
458             if (card->getParentCard()) {
459                 card->setParentCard(nullptr);
460             }
461 
462             // Make a copy of the list because the original one gets modified during the loop
463             QList<Server_Card *> attachedCards = card->getAttachedCards();
464             for (auto &attachedCard : attachedCards) {
465                 attachedCard->getZone()->getPlayer()->unattachCard(ges, attachedCard);
466             }
467         }
468 
469         if (startzone != targetzone) {
470             // Delete all arrows from and to the card
471             const QList<Server_Player *> &players = game->getPlayers().values();
472             for (auto player : players) {
473                 QList<int> arrowsToDelete;
474                 QMapIterator<int, Server_Arrow *> arrowIterator(player->getArrows());
475                 while (arrowIterator.hasNext()) {
476                     Server_Arrow *arrow = arrowIterator.next().value();
477                     if ((arrow->getStartCard() == card) || (arrow->getTargetItem() == card))
478                         arrowsToDelete.append(arrow->getId());
479                 }
480                 for (int j : arrowsToDelete) {
481                     player->deleteArrow(j);
482                 }
483             }
484         }
485 
486         if (card->getDestroyOnZoneChange() && (startzone->getName() != targetzone->getName())) {
487             Event_DestroyCard event;
488             event.set_zone_name(startzone->getName().toStdString());
489             event.set_card_id(static_cast<google::protobuf::uint32>(card->getId()));
490             ges.enqueueGameEvent(event, playerId);
491 
492             card->deleteLater();
493         } else {
494             if (!targetzone->hasCoords()) {
495                 y = 0;
496                 card->resetState();
497             } else {
498                 newX = targetzone->getFreeGridColumn(newX, y, card->getName(), faceDown);
499             }
500 
501             targetzone->insertCard(card, newX, y);
502 
503             bool targetBeingLookedAt = (targetzone->getType() != ServerInfo_Zone::HiddenZone) ||
504                                        (targetzone->getCardsBeingLookedAt() > newX) ||
505                                        (targetzone->getCardsBeingLookedAt() == -1);
506             bool sourceBeingLookedAt = (startzone->getType() != ServerInfo_Zone::HiddenZone) ||
507                                        (startzone->getCardsBeingLookedAt() > position) ||
508                                        (startzone->getCardsBeingLookedAt() == -1);
509 
510             bool targetHiddenToPlayer = faceDown || !targetBeingLookedAt;
511             bool targetHiddenToOthers = faceDown || (targetzone->getType() != ServerInfo_Zone::PublicZone);
512             bool sourceHiddenToPlayer = card->getFaceDown() || !sourceBeingLookedAt;
513             bool sourceHiddenToOthers = card->getFaceDown() || (startzone->getType() != ServerInfo_Zone::PublicZone);
514 
515             QString privateCardName, publicCardName;
516             if (!(sourceHiddenToPlayer && targetHiddenToPlayer)) {
517                 privateCardName = card->getName();
518             }
519             if (!(sourceHiddenToOthers && targetHiddenToOthers)) {
520                 publicCardName = card->getName();
521             }
522 
523             int oldCardId = card->getId();
524             if ((faceDown && (startzone != targetzone)) || (targetzone->getPlayer() != startzone->getPlayer())) {
525                 card->setId(targetzone->getPlayer()->newCardId());
526             }
527             card->setFaceDown(faceDown);
528 
529             // The player does not get to see which card he moved if it moves between two parts of hidden zones which
530             // are not being looked at.
531             int privateNewCardId = card->getId();
532             int privateOldCardId = oldCardId;
533             if (!targetBeingLookedAt && !sourceBeingLookedAt) {
534                 privateOldCardId = -1;
535                 privateNewCardId = -1;
536                 privateCardName = QString();
537             }
538             int privatePosition = -1;
539             if (startzone->getType() == ServerInfo_Zone::HiddenZone) {
540                 privatePosition = position;
541             }
542 
543             int publicNewX = newX;
544 
545             Event_MoveCard eventOthers;
546             eventOthers.set_start_player_id(startzone->getPlayer()->getPlayerId());
547             eventOthers.set_start_zone(startzone->getName().toStdString());
548             eventOthers.set_target_player_id(targetzone->getPlayer()->getPlayerId());
549             if (startzone != targetzone) {
550                 eventOthers.set_target_zone(targetzone->getName().toStdString());
551             }
552             eventOthers.set_y(y);
553             eventOthers.set_face_down(faceDown);
554 
555             Event_MoveCard eventPrivate(eventOthers);
556             eventPrivate.set_card_id(privateOldCardId);
557             if (!privateCardName.isEmpty()) {
558                 eventPrivate.set_card_name(privateCardName.toStdString());
559             }
560             eventPrivate.set_position(privatePosition);
561             eventPrivate.set_new_card_id(privateNewCardId);
562             eventPrivate.set_x(newX);
563 
564             // Other players do not get to see the start and/or target position of the card if the respective
565             // part of the zone is being looked at. The information is not needed anyway because in hidden zones,
566             // all cards are equal.
567             if (((startzone->getType() == ServerInfo_Zone::HiddenZone) &&
568                  ((startzone->getCardsBeingLookedAt() > position) || (startzone->getCardsBeingLookedAt() == -1))) ||
569                 (startzone->getType() == ServerInfo_Zone::PublicZone)) {
570                 position = -1;
571             }
572             if ((targetzone->getType() == ServerInfo_Zone::HiddenZone) &&
573                 ((targetzone->getCardsBeingLookedAt() > newX) || (targetzone->getCardsBeingLookedAt() == -1))) {
574                 publicNewX = -1;
575             }
576 
577             eventOthers.set_x(publicNewX);
578             eventOthers.set_position(position);
579             if ((startzone->getType() == ServerInfo_Zone::PublicZone) ||
580                 (targetzone->getType() == ServerInfo_Zone::PublicZone)) {
581                 eventOthers.set_card_id(oldCardId);
582                 if (!publicCardName.isEmpty()) {
583                     eventOthers.set_card_name(publicCardName.toStdString());
584                 }
585                 eventOthers.set_new_card_id(card->getId());
586             }
587 
588             ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
589             ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
590 
591             if (thisCardProperties->tapped()) {
592                 setCardAttrHelper(ges, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), card->getId(),
593                                   AttrTapped, "1");
594             }
595             QString ptString = QString::fromStdString(thisCardProperties->pt());
596             if (!ptString.isEmpty()) {
597                 setCardAttrHelper(ges, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), card->getId(),
598                                   AttrPT, ptString);
599             }
600         }
601         if (startzone->getAlwaysRevealTopCard() && !startzone->getCards().isEmpty() && (originalPosition == 0)) {
602             Event_RevealCards revealEvent;
603             revealEvent.set_zone_name(startzone->getName().toStdString());
604             revealEvent.set_card_id(0);
605             startzone->getCards().first()->getInfo(revealEvent.add_cards());
606 
607             ges.enqueueGameEvent(revealEvent, playerId);
608         }
609         if (targetzone->getAlwaysRevealTopCard() && !targetzone->getCards().isEmpty() && (newX == 0)) {
610             Event_RevealCards revealEvent;
611             revealEvent.set_zone_name(targetzone->getName().toStdString());
612             revealEvent.set_card_id(0);
613             targetzone->getCards().first()->getInfo(revealEvent.add_cards());
614 
615             ges.enqueueGameEvent(revealEvent, playerId);
616         }
617     }
618     if (undoingDraw) {
619         ges.setGameEventContext(Context_UndoDraw());
620     } else {
621         ges.setGameEventContext(Context_MoveCard());
622     }
623 
624     if (startzone->hasCoords() && fixFreeSpaces) {
625         startzone->fixFreeSpaces(ges);
626     }
627 
628     return Response::RespOk;
629 }
630 
unattachCard(GameEventStorage & ges,Server_Card * card)631 void Server_Player::unattachCard(GameEventStorage &ges, Server_Card *card)
632 {
633     Server_CardZone *zone = card->getZone();
634     Server_Card *parentCard = card->getParentCard();
635     card->setParentCard(nullptr);
636 
637     Event_AttachCard event;
638     event.set_start_zone(zone->getName().toStdString());
639     event.set_card_id(card->getId());
640     ges.enqueueGameEvent(event, playerId);
641 
642     auto *cardToMove = new CardToMove;
643     cardToMove->set_card_id(card->getId());
644     moveCard(ges, zone, QList<const CardToMove *>() << cardToMove, zone, -1, card->getY(), card->getFaceDown());
645     delete cardToMove;
646 
647     if (parentCard->getZone()) {
648         parentCard->getZone()->updateCardCoordinates(parentCard, parentCard->getX(), parentCard->getY());
649     }
650 }
651 
setCardAttrHelper(GameEventStorage & ges,int targetPlayerId,const QString & zoneName,int cardId,CardAttribute attribute,const QString & attrValue)652 Response::ResponseCode Server_Player::setCardAttrHelper(GameEventStorage &ges,
653                                                         int targetPlayerId,
654                                                         const QString &zoneName,
655                                                         int cardId,
656                                                         CardAttribute attribute,
657                                                         const QString &attrValue)
658 {
659     Server_CardZone *zone = getZones().value(zoneName);
660     if (!zone) {
661         return Response::RespNameNotFound;
662     }
663     if (!zone->hasCoords()) {
664         return Response::RespContextError;
665     }
666 
667     QString result;
668     if (cardId == -1) {
669         QListIterator<Server_Card *> CardIterator(zone->getCards());
670         while (CardIterator.hasNext()) {
671             result = CardIterator.next()->setAttribute(attribute, attrValue, true);
672             if (result.isNull()) {
673                 return Response::RespInvalidCommand;
674             }
675         }
676     } else {
677         Server_Card *card = zone->getCard(cardId);
678         if (!card) {
679             return Response::RespNameNotFound;
680         }
681         result = card->setAttribute(attribute, attrValue, false);
682         if (result.isNull()) {
683             return Response::RespInvalidCommand;
684         }
685     }
686 
687     Event_SetCardAttr event;
688     event.set_zone_name(zone->getName().toStdString());
689     if (cardId != -1) {
690         event.set_card_id(cardId);
691     }
692     event.set_attribute(attribute);
693     event.set_attr_value(result.toStdString());
694     ges.enqueueGameEvent(event, targetPlayerId);
695 
696     return Response::RespOk;
697 }
698 
699 Response::ResponseCode
cmdLeaveGame(const Command_LeaveGame &,ResponseContainer &,GameEventStorage &)700 Server_Player::cmdLeaveGame(const Command_LeaveGame & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/)
701 {
702     game->removePlayer(this, Event_Leave::USER_LEFT);
703     return Response::RespOk;
704 }
705 
706 Response::ResponseCode
cmdKickFromGame(const Command_KickFromGame & cmd,ResponseContainer &,GameEventStorage &)707 Server_Player::cmdKickFromGame(const Command_KickFromGame &cmd, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/)
708 {
709     if ((game->getHostId() != playerId) && !(userInfo->user_level() & ServerInfo_User::IsModerator)) {
710         return Response::RespFunctionNotAllowed;
711     }
712 
713     if (!game->kickPlayer(cmd.player_id())) {
714         return Response::RespNameNotFound;
715     }
716 
717     return Response::RespOk;
718 }
719 
720 Response::ResponseCode
cmdDeckSelect(const Command_DeckSelect & cmd,ResponseContainer & rc,GameEventStorage & ges)721 Server_Player::cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &rc, GameEventStorage &ges)
722 {
723     if (spectator) {
724         return Response::RespFunctionNotAllowed;
725     }
726 
727     DeckList *newDeck;
728     if (cmd.has_deck_id()) {
729         try {
730             newDeck = game->getRoom()->getServer()->getDatabaseInterface()->getDeckFromDatabase(cmd.deck_id(),
731                                                                                                 userInfo->id());
732         } catch (Response::ResponseCode &r) {
733             return r;
734         }
735     } else {
736         newDeck = new DeckList(QString::fromStdString(cmd.deck()));
737     }
738 
739     if (!newDeck) {
740         return Response::RespInternalError;
741     }
742 
743     delete deck;
744     deck = newDeck;
745     sideboardLocked = true;
746 
747     Event_PlayerPropertiesChanged event;
748     event.mutable_player_properties()->set_sideboard_locked(true);
749     event.mutable_player_properties()->set_deck_hash(deck->getDeckHash().toStdString());
750     ges.enqueueGameEvent(event, playerId);
751 
752     Context_DeckSelect context;
753     context.set_deck_hash(deck->getDeckHash().toStdString());
754     context.set_sideboard_size(deck->getSideboardSize());
755     ges.setGameEventContext(context);
756 
757     auto *re = new Response_DeckDownload;
758     re->set_deck(deck->writeToString_Native().toStdString());
759 
760     rc.setResponseExtension(re);
761     return Response::RespOk;
762 }
763 
cmdSetSideboardPlan(const Command_SetSideboardPlan & cmd,ResponseContainer &,GameEventStorage &)764 Response::ResponseCode Server_Player::cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd,
765                                                           ResponseContainer & /*rc*/,
766                                                           GameEventStorage & /*ges*/)
767 {
768     if (spectator) {
769         return Response::RespFunctionNotAllowed;
770     }
771     if (readyStart) {
772         return Response::RespContextError;
773     }
774     if (!deck) {
775         return Response::RespContextError;
776     }
777     if (sideboardLocked) {
778         return Response::RespContextError;
779     }
780 
781     QList<MoveCard_ToZone> sideboardPlan;
782     for (int i = 0; i < cmd.move_list_size(); ++i) {
783         sideboardPlan.append(cmd.move_list(i));
784     }
785     deck->setCurrentSideboardPlan(sideboardPlan);
786 
787     return Response::RespOk;
788 }
789 
cmdSetSideboardLock(const Command_SetSideboardLock & cmd,ResponseContainer &,GameEventStorage & ges)790 Response::ResponseCode Server_Player::cmdSetSideboardLock(const Command_SetSideboardLock &cmd,
791                                                           ResponseContainer & /*rc*/,
792                                                           GameEventStorage &ges)
793 {
794     if (spectator) {
795         return Response::RespFunctionNotAllowed;
796     }
797     if (readyStart) {
798         return Response::RespContextError;
799     }
800     if (!deck) {
801         return Response::RespContextError;
802     }
803     if (sideboardLocked == cmd.locked()) {
804         return Response::RespContextError;
805     }
806 
807     sideboardLocked = cmd.locked();
808     if (sideboardLocked) {
809         deck->setCurrentSideboardPlan(QList<MoveCard_ToZone>());
810     }
811 
812     Event_PlayerPropertiesChanged event;
813     event.mutable_player_properties()->set_sideboard_locked(sideboardLocked);
814     ges.enqueueGameEvent(event, playerId);
815     ges.setGameEventContext(Context_SetSideboardLock());
816 
817     return Response::RespOk;
818 }
819 
820 Response::ResponseCode
cmdConcede(const Command_Concede &,ResponseContainer &,GameEventStorage & ges)821 Server_Player::cmdConcede(const Command_Concede & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges)
822 {
823     if (spectator) {
824         return Response::RespFunctionNotAllowed;
825     }
826     if (!game->getGameStarted()) {
827         return Response::RespGameNotStarted;
828     }
829     if (conceded) {
830         return Response::RespContextError;
831     }
832 
833     setConceded(true);
834     game->removeArrowsRelatedToPlayer(ges, this);
835     game->unattachCards(ges, this);
836     clearZones();
837 
838     Event_PlayerPropertiesChanged event;
839     event.mutable_player_properties()->set_conceded(true);
840     ges.enqueueGameEvent(event, playerId);
841     ges.setGameEventContext(Context_Concede());
842 
843     game->stopGameIfFinished();
844     if (game->getGameStarted() && (game->getActivePlayer() == playerId)) {
845         game->nextTurn();
846     }
847 
848     return Response::RespOk;
849 }
850 
851 Response::ResponseCode
cmdUnconcede(const Command_Unconcede &,ResponseContainer &,GameEventStorage & ges)852 Server_Player::cmdUnconcede(const Command_Unconcede & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges)
853 {
854     if (spectator) {
855         return Response::RespFunctionNotAllowed;
856     }
857     if (!game->getGameStarted()) {
858         return Response::RespGameNotStarted;
859     }
860     if (!conceded) {
861         return Response::RespContextError;
862     }
863 
864     setConceded(false);
865 
866     Event_PlayerPropertiesChanged event;
867     event.mutable_player_properties()->set_conceded(false);
868     ges.enqueueGameEvent(event, playerId);
869     ges.setGameEventContext(Context_Unconcede());
870 
871     setupZones();
872 
873     game->sendGameStateToPlayers();
874 
875     return Response::RespOk;
876 }
877 
cmdJudge(const Command_Judge & cmd,ResponseContainer & rc,GameEventStorage & ges)878 Response::ResponseCode Server_Player::cmdJudge(const Command_Judge &cmd, ResponseContainer &rc, GameEventStorage &ges)
879 {
880     if (!judge)
881         return Response::RespFunctionNotAllowed;
882 
883     Server_Player *player = this->game->getPlayers().value(cmd.target_id());
884 
885     ges.setForcedByJudge(playerId);
886     if (player == nullptr)
887         return Response::RespContextError;
888 
889     for (int i = 0; i < cmd.game_command_size(); ++i) {
890         player->processGameCommand(cmd.game_command(i), rc, ges);
891     }
892 
893     return Response::RespOk;
894 }
895 
896 Response::ResponseCode
cmdReadyStart(const Command_ReadyStart & cmd,ResponseContainer &,GameEventStorage & ges)897 Server_Player::cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
898 {
899     if (spectator) {
900         return Response::RespFunctionNotAllowed;
901     }
902 
903     if (!deck || game->getGameStarted()) {
904         return Response::RespContextError;
905     }
906 
907     if (readyStart == cmd.ready()) {
908         return Response::RespContextError;
909     }
910 
911     setReadyStart(cmd.ready());
912 
913     Event_PlayerPropertiesChanged event;
914     event.mutable_player_properties()->set_ready_start(cmd.ready());
915     ges.enqueueGameEvent(event, playerId);
916     ges.setGameEventContext(Context_ReadyStart());
917 
918     if (cmd.ready()) {
919         game->startGameIfReady();
920     }
921 
922     return Response::RespOk;
923 }
924 
925 Response::ResponseCode
cmdGameSay(const Command_GameSay & cmd,ResponseContainer &,GameEventStorage & ges)926 Server_Player::cmdGameSay(const Command_GameSay &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
927 {
928     if (spectator) {
929         /* Spectators can only talk if:
930          * (a) the game creator allows it
931          * (b) the spectator is a moderator/administrator
932          * (c) the spectator is a judge
933          */
934         bool isModOrJudge = (userInfo->user_level() & (ServerInfo_User::IsModerator | ServerInfo_User::IsJudge));
935         if (!isModOrJudge && !game->getSpectatorsCanTalk()) {
936             return Response::RespFunctionNotAllowed;
937         }
938     }
939 
940     Event_GameSay event;
941     event.set_message(cmd.message());
942     ges.enqueueGameEvent(event, playerId);
943 
944     game->getRoom()->getServer()->getDatabaseInterface()->logMessage(
945         userInfo->id(), QString::fromStdString(userInfo->name()), QString::fromStdString(userInfo->address()),
946         QString::fromStdString(cmd.message()), Server_DatabaseInterface::MessageTargetGame, game->getGameId(),
947         game->getDescription());
948 
949     return Response::RespOk;
950 }
951 
952 Response::ResponseCode
cmdShuffle(const Command_Shuffle & cmd,ResponseContainer &,GameEventStorage & ges)953 Server_Player::cmdShuffle(const Command_Shuffle &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
954 {
955     if (spectator) {
956         return Response::RespFunctionNotAllowed;
957     }
958 
959     if (!game->getGameStarted()) {
960         return Response::RespGameNotStarted;
961     }
962 
963     if (conceded) {
964         return Response::RespContextError;
965     }
966 
967     if (cmd.has_zone_name() && cmd.zone_name() != "deck") {
968         return Response::RespFunctionNotAllowed;
969     }
970 
971     Server_CardZone *zone = zones.value("deck");
972     if (!zone) {
973         return Response::RespNameNotFound;
974     }
975 
976     zone->shuffle(cmd.start(), cmd.end());
977 
978     Event_Shuffle event;
979     event.set_zone_name(zone->getName().toStdString());
980     event.set_start(cmd.start());
981     event.set_end(cmd.end());
982     ges.enqueueGameEvent(event, playerId);
983 
984     if (zone->getAlwaysRevealTopCard() && !zone->getCards().isEmpty()) {
985         Event_RevealCards revealEvent;
986         revealEvent.set_zone_name(zone->getName().toStdString());
987         revealEvent.set_card_id(0);
988         zone->getCards().first()->getInfo(revealEvent.add_cards());
989 
990         ges.enqueueGameEvent(revealEvent, playerId);
991     }
992 
993     return Response::RespOk;
994 }
995 
996 Response::ResponseCode
cmdMulligan(const Command_Mulligan & cmd,ResponseContainer &,GameEventStorage & ges)997 Server_Player::cmdMulligan(const Command_Mulligan &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
998 {
999     if (spectator) {
1000         return Response::RespFunctionNotAllowed;
1001     }
1002 
1003     if (!game->getGameStarted()) {
1004         return Response::RespGameNotStarted;
1005     }
1006     if (conceded) {
1007         return Response::RespContextError;
1008     }
1009 
1010     Server_CardZone *hand = zones.value("hand");
1011     Server_CardZone *deck = zones.value("deck");
1012     int number = cmd.number();
1013 
1014     if (!hand->getCards().isEmpty()) {
1015         auto cardsToMove = QList<const CardToMove *>();
1016         for (auto &card : hand->getCards()) {
1017             auto *cardToMove = new CardToMove;
1018             cardToMove->set_card_id(card->getId());
1019             cardsToMove.append(cardToMove);
1020         }
1021         moveCard(ges, hand, cardsToMove, deck, -1, 0, false);
1022         qDeleteAll(cardsToMove);
1023     }
1024 
1025     deck->shuffle();
1026     ges.enqueueGameEvent(Event_Shuffle(), playerId);
1027 
1028     drawCards(ges, number);
1029 
1030     Context_Mulligan context;
1031     context.set_number(static_cast<google::protobuf::uint32>(hand->getCards().size()));
1032     ges.setGameEventContext(context);
1033 
1034     return Response::RespOk;
1035 }
1036 
1037 Response::ResponseCode
cmdRollDie(const Command_RollDie & cmd,ResponseContainer &,GameEventStorage & ges)1038 Server_Player::cmdRollDie(const Command_RollDie &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1039 {
1040     if (spectator) {
1041         return Response::RespFunctionNotAllowed;
1042     }
1043     if (conceded) {
1044         return Response::RespContextError;
1045     }
1046 
1047     Event_RollDie event;
1048     event.set_sides(cmd.sides());
1049     event.set_value(rng->rand(1, cmd.sides()));
1050     ges.enqueueGameEvent(event, playerId);
1051 
1052     return Response::RespOk;
1053 }
1054 
1055 Response::ResponseCode
cmdDrawCards(const Command_DrawCards & cmd,ResponseContainer &,GameEventStorage & ges)1056 Server_Player::cmdDrawCards(const Command_DrawCards &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1057 {
1058     if (spectator) {
1059         return Response::RespFunctionNotAllowed;
1060     }
1061 
1062     if (!game->getGameStarted()) {
1063         return Response::RespGameNotStarted;
1064     }
1065     if (conceded) {
1066         return Response::RespContextError;
1067     }
1068 
1069     return drawCards(ges, cmd.number());
1070 }
1071 
1072 Response::ResponseCode
cmdUndoDraw(const Command_UndoDraw &,ResponseContainer &,GameEventStorage & ges)1073 Server_Player::cmdUndoDraw(const Command_UndoDraw & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges)
1074 {
1075     if (spectator) {
1076         return Response::RespFunctionNotAllowed;
1077     }
1078 
1079     if (!game->getGameStarted()) {
1080         return Response::RespGameNotStarted;
1081     }
1082     if (conceded) {
1083         return Response::RespContextError;
1084     }
1085 
1086     if (lastDrawList.isEmpty()) {
1087         return Response::RespContextError;
1088     }
1089 
1090     Response::ResponseCode retVal;
1091     auto *cardToMove = new CardToMove;
1092     cardToMove->set_card_id(lastDrawList.takeLast());
1093     retVal = moveCard(ges, zones.value("hand"), QList<const CardToMove *>() << cardToMove, zones.value("deck"), 0, 0,
1094                       false, true);
1095     delete cardToMove;
1096 
1097     return retVal;
1098 }
1099 
1100 Response::ResponseCode
cmdMoveCard(const Command_MoveCard & cmd,ResponseContainer &,GameEventStorage & ges)1101 Server_Player::cmdMoveCard(const Command_MoveCard &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1102 {
1103     if (spectator) {
1104         return Response::RespFunctionNotAllowed;
1105     }
1106 
1107     if (!game->getGameStarted()) {
1108         return Response::RespGameNotStarted;
1109     }
1110     if (conceded) {
1111         return Response::RespContextError;
1112     }
1113 
1114     Server_Player *startPlayer = game->getPlayers().value(cmd.has_start_player_id() ? cmd.start_player_id() : playerId);
1115     if (!startPlayer) {
1116         return Response::RespNameNotFound;
1117     }
1118     Server_CardZone *startZone = startPlayer->getZones().value(QString::fromStdString(cmd.start_zone()));
1119     if (!startZone) {
1120         return Response::RespNameNotFound;
1121     }
1122 
1123     if ((startPlayer != this) && (!startZone->getPlayersWithWritePermission().contains(playerId)) && !judge) {
1124         return Response::RespContextError;
1125     }
1126 
1127     Server_Player *targetPlayer = game->getPlayers().value(cmd.target_player_id());
1128     if (!targetPlayer) {
1129         return Response::RespNameNotFound;
1130     }
1131     Server_CardZone *targetZone = targetPlayer->getZones().value(QString::fromStdString(cmd.target_zone()));
1132     if (!targetZone) {
1133         return Response::RespNameNotFound;
1134     }
1135 
1136     if ((startPlayer != this) && (targetPlayer != this) && !judge) {
1137         return Response::RespContextError;
1138     }
1139 
1140     QList<const CardToMove *> cardsToMove;
1141     for (int i = 0; i < cmd.cards_to_move().card_size(); ++i) {
1142         cardsToMove.append(&cmd.cards_to_move().card(i));
1143     }
1144 
1145     return moveCard(ges, startZone, cardsToMove, targetZone, cmd.x(), cmd.y());
1146 }
1147 
1148 Response::ResponseCode
cmdFlipCard(const Command_FlipCard & cmd,ResponseContainer &,GameEventStorage & ges)1149 Server_Player::cmdFlipCard(const Command_FlipCard &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1150 {
1151     if (spectator) {
1152         return Response::RespFunctionNotAllowed;
1153     }
1154 
1155     if (!game->getGameStarted()) {
1156         return Response::RespGameNotStarted;
1157     }
1158     if (conceded) {
1159         return Response::RespContextError;
1160     }
1161 
1162     Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone()));
1163     if (!zone) {
1164         return Response::RespNameNotFound;
1165     }
1166     if (!zone->hasCoords()) {
1167         return Response::RespContextError;
1168     }
1169 
1170     Server_Card *card = zone->getCard(cmd.card_id());
1171     if (!card) {
1172         return Response::RespNameNotFound;
1173     }
1174 
1175     const bool faceDown = cmd.face_down();
1176     if (faceDown == card->getFaceDown()) {
1177         return Response::RespContextError;
1178     }
1179 
1180     card->setFaceDown(faceDown);
1181 
1182     Event_FlipCard event;
1183     event.set_zone_name(zone->getName().toStdString());
1184     event.set_card_id(card->getId());
1185     if (!faceDown) {
1186         event.set_card_name(card->getName().toStdString());
1187     }
1188     event.set_face_down(faceDown);
1189     ges.enqueueGameEvent(event, playerId);
1190 
1191     QString ptString = QString::fromStdString(cmd.pt());
1192     if (!ptString.isEmpty() && !faceDown) {
1193         setCardAttrHelper(ges, playerId, zone->getName(), card->getId(), AttrPT, ptString);
1194     }
1195 
1196     return Response::RespOk;
1197 }
1198 
1199 Response::ResponseCode
cmdAttachCard(const Command_AttachCard & cmd,ResponseContainer &,GameEventStorage & ges)1200 Server_Player::cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1201 {
1202     if (spectator) {
1203         return Response::RespFunctionNotAllowed;
1204     }
1205 
1206     if (!game->getGameStarted()) {
1207         return Response::RespGameNotStarted;
1208     }
1209     if (conceded) {
1210         return Response::RespContextError;
1211     }
1212 
1213     Server_CardZone *startzone = zones.value(QString::fromStdString(cmd.start_zone()));
1214     if (!startzone) {
1215         return Response::RespNameNotFound;
1216     }
1217 
1218     Server_Card *card = startzone->getCard(cmd.card_id());
1219     if (!card) {
1220         return Response::RespNameNotFound;
1221     }
1222 
1223     Server_Player *targetPlayer = nullptr;
1224     Server_CardZone *targetzone = nullptr;
1225     Server_Card *targetCard = nullptr;
1226 
1227     if (cmd.has_target_player_id()) {
1228         targetPlayer = game->getPlayers().value(cmd.target_player_id());
1229         if (!targetPlayer) {
1230             return Response::RespNameNotFound;
1231         }
1232     } else if (!card->getParentCard()) {
1233         return Response::RespContextError;
1234     }
1235     if (targetPlayer) {
1236         targetzone = targetPlayer->getZones().value(QString::fromStdString(cmd.target_zone()));
1237     }
1238     if (targetzone) {
1239         // This is currently enough to make sure cards don't get attached to a card that is not on the table.
1240         // Possibly a flag will have to be introduced for this sometime.
1241         if (!targetzone->hasCoords()) {
1242             return Response::RespContextError;
1243         }
1244         if (cmd.has_target_card_id()) {
1245             targetCard = targetzone->getCard(cmd.target_card_id());
1246         }
1247         if (targetCard) {
1248             if (targetCard->getParentCard()) {
1249                 return Response::RespContextError;
1250             }
1251         } else {
1252             return Response::RespNameNotFound;
1253         }
1254     }
1255     if (!startzone->hasCoords()) {
1256         return Response::RespContextError;
1257     }
1258 
1259     // Get all arrows pointing to or originating from the card being attached and delete them.
1260     QMapIterator<int, Server_Player *> playerIterator(game->getPlayers());
1261     while (playerIterator.hasNext()) {
1262         Server_Player *p = playerIterator.next().value();
1263         QList<Server_Arrow *> arrows = p->getArrows().values();
1264         QList<Server_Arrow *> toDelete;
1265         for (auto a : arrows) {
1266             auto *tCard = qobject_cast<Server_Card *>(a->getTargetItem());
1267             if ((tCard == card) || (a->getStartCard() == card)) {
1268                 toDelete.append(a);
1269             }
1270         }
1271         for (auto &i : toDelete) {
1272             Event_DeleteArrow event;
1273             event.set_arrow_id(i->getId());
1274             ges.enqueueGameEvent(event, p->getPlayerId());
1275             p->deleteArrow(i->getId());
1276         }
1277     }
1278 
1279     if (targetCard) {
1280         // Unattach all cards attached to the card being attached.
1281         // Make a copy of the list because its contents change during the loop otherwise.
1282         QList<Server_Card *> attachedList = card->getAttachedCards();
1283         for (const auto &i : attachedList) {
1284             i->getZone()->getPlayer()->unattachCard(ges, i);
1285         }
1286 
1287         card->setParentCard(targetCard);
1288         const int oldX = card->getX();
1289         card->setCoords(-1, card->getY());
1290         startzone->updateCardCoordinates(card, oldX, card->getY());
1291 
1292         if (targetzone->isColumnStacked(targetCard->getX(), targetCard->getY())) {
1293             auto *cardToMove = new CardToMove;
1294             cardToMove->set_card_id(targetCard->getId());
1295             targetPlayer->moveCard(ges, targetzone, QList<const CardToMove *>() << cardToMove, targetzone,
1296                                    targetzone->getFreeGridColumn(-2, targetCard->getY(), targetCard->getName(), false),
1297                                    targetCard->getY(), targetCard->getFaceDown());
1298             delete cardToMove;
1299         }
1300 
1301         Event_AttachCard event;
1302         event.set_start_zone(startzone->getName().toStdString());
1303         event.set_card_id(card->getId());
1304         event.set_target_player_id(targetPlayer->getPlayerId());
1305         event.set_target_zone(targetzone->getName().toStdString());
1306         event.set_target_card_id(targetCard->getId());
1307         ges.enqueueGameEvent(event, playerId);
1308 
1309         startzone->fixFreeSpaces(ges);
1310     } else {
1311         unattachCard(ges, card);
1312     }
1313 
1314     return Response::RespOk;
1315 }
1316 
1317 Response::ResponseCode
cmdCreateToken(const Command_CreateToken & cmd,ResponseContainer & rc,GameEventStorage & ges)1318 Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer &rc, GameEventStorage &ges)
1319 {
1320     if (spectator) {
1321         return Response::RespFunctionNotAllowed;
1322     }
1323 
1324     if (!game->getGameStarted()) {
1325         return Response::RespGameNotStarted;
1326     }
1327     if (conceded) {
1328         return Response::RespContextError;
1329     }
1330 
1331     Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone()));
1332     if (!zone) {
1333         return Response::RespNameNotFound;
1334     }
1335 
1336     QString cardName = QString::fromStdString(cmd.card_name());
1337     int x = cmd.x();
1338     int y = cmd.y();
1339     if (zone->hasCoords()) {
1340         x = zone->getFreeGridColumn(x, y, cardName, false);
1341     }
1342     if (x < 0) {
1343         x = 0;
1344     }
1345     if (y < 0) {
1346         y = 0;
1347     }
1348 
1349     auto *card = new Server_Card(cardName, newCardId(), x, y);
1350     card->moveToThread(thread());
1351     card->setPT(QString::fromStdString(cmd.pt()));
1352     card->setColor(QString::fromStdString(cmd.color()));
1353     card->setAnnotation(QString::fromStdString(cmd.annotation()));
1354     card->setDestroyOnZoneChange(cmd.destroy_on_zone_change());
1355 
1356     zone->insertCard(card, x, y);
1357 
1358     Event_CreateToken event;
1359     event.set_zone_name(zone->getName().toStdString());
1360     event.set_card_id(card->getId());
1361     event.set_card_name(card->getName().toStdString());
1362     event.set_color(card->getColor().toStdString());
1363     event.set_pt(card->getPT().toStdString());
1364     event.set_annotation(card->getAnnotation().toStdString());
1365     event.set_destroy_on_zone_change(card->getDestroyOnZoneChange());
1366     event.set_x(x);
1367     event.set_y(y);
1368     ges.enqueueGameEvent(event, playerId);
1369 
1370     // check if the token is a replacement for an existing card
1371     if (cmd.target_card_id() < 0) {
1372         return Response::RespOk;
1373     }
1374 
1375     Command_AttachCard cmd2;
1376     cmd2.set_start_zone(cmd.target_zone());
1377     cmd2.set_card_id(cmd.target_card_id());
1378 
1379     cmd2.set_target_player_id(zone->getPlayer()->getPlayerId());
1380     cmd2.set_target_zone(cmd.zone());
1381     cmd2.set_target_card_id(card->getId());
1382 
1383     return cmdAttachCard(cmd2, rc, ges);
1384 }
1385 
1386 Response::ResponseCode
cmdCreateArrow(const Command_CreateArrow & cmd,ResponseContainer &,GameEventStorage & ges)1387 Server_Player::cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1388 {
1389     if (spectator) {
1390         return Response::RespFunctionNotAllowed;
1391     }
1392 
1393     if (!game->getGameStarted()) {
1394         return Response::RespGameNotStarted;
1395     }
1396     if (conceded) {
1397         return Response::RespContextError;
1398     }
1399 
1400     Server_Player *startPlayer = game->getPlayers().value(cmd.start_player_id());
1401     Server_Player *targetPlayer = game->getPlayers().value(cmd.target_player_id());
1402     if (!startPlayer || !targetPlayer) {
1403         return Response::RespNameNotFound;
1404     }
1405     QString startZoneName = QString::fromStdString(cmd.start_zone());
1406     Server_CardZone *startZone = startPlayer->getZones().value(startZoneName);
1407     bool playerTarget = !cmd.has_target_zone();
1408     Server_CardZone *targetZone = nullptr;
1409     if (!playerTarget) {
1410         targetZone = targetPlayer->getZones().value(QString::fromStdString(cmd.target_zone()));
1411     }
1412     if (!startZone || (!targetZone && !playerTarget)) {
1413         return Response::RespNameNotFound;
1414     }
1415     if (startZone->getType() != ServerInfo_Zone::PublicZone) {
1416         return Response::RespContextError;
1417     }
1418     Server_Card *startCard = startZone->getCard(cmd.start_card_id());
1419     if (!startCard) {
1420         return Response::RespNameNotFound;
1421     }
1422     Server_Card *targetCard = nullptr;
1423     if (!playerTarget) {
1424         if (targetZone->getType() != ServerInfo_Zone::PublicZone) {
1425             return Response::RespContextError;
1426         }
1427         targetCard = targetZone->getCard(cmd.target_card_id());
1428     }
1429 
1430     Server_ArrowTarget *targetItem;
1431     if (playerTarget) {
1432         targetItem = targetPlayer;
1433     } else {
1434         targetItem = targetCard;
1435     }
1436     if (!targetItem) {
1437         return Response::RespNameNotFound;
1438     }
1439 
1440     QMapIterator<int, Server_Arrow *> arrowIterator(arrows);
1441     while (arrowIterator.hasNext()) {
1442         Server_Arrow *temp = arrowIterator.next().value();
1443         if ((temp->getStartCard() == startCard) && (temp->getTargetItem() == targetItem)) {
1444             return Response::RespContextError;
1445         }
1446     }
1447 
1448     auto arrow = new Server_Arrow(newArrowId(), startCard, targetItem, cmd.arrow_color());
1449     addArrow(arrow);
1450 
1451     Event_CreateArrow event;
1452     ServerInfo_Arrow *arrowInfo = event.mutable_arrow_info();
1453     arrowInfo->set_id(arrow->getId());
1454     arrowInfo->set_start_player_id(startPlayer->getPlayerId());
1455     arrowInfo->set_start_zone(startZoneName.toStdString());
1456     arrowInfo->set_start_card_id(startCard->getId());
1457     arrowInfo->set_target_player_id(targetPlayer->getPlayerId());
1458     if (!playerTarget) {
1459         arrowInfo->set_target_zone(cmd.target_zone());
1460         arrowInfo->set_target_card_id(cmd.target_card_id());
1461     }
1462     arrowInfo->mutable_arrow_color()->CopyFrom(cmd.arrow_color());
1463     ges.enqueueGameEvent(event, playerId);
1464 
1465     return Response::RespOk;
1466 }
1467 
1468 Response::ResponseCode
cmdDeleteArrow(const Command_DeleteArrow & cmd,ResponseContainer &,GameEventStorage & ges)1469 Server_Player::cmdDeleteArrow(const Command_DeleteArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1470 {
1471     if (spectator) {
1472         return Response::RespFunctionNotAllowed;
1473     }
1474 
1475     if (!game->getGameStarted()) {
1476         return Response::RespGameNotStarted;
1477     }
1478     if (conceded) {
1479         return Response::RespContextError;
1480     }
1481 
1482     if (!deleteArrow(cmd.arrow_id())) {
1483         return Response::RespNameNotFound;
1484     }
1485 
1486     Event_DeleteArrow event;
1487     event.set_arrow_id(cmd.arrow_id());
1488     ges.enqueueGameEvent(event, playerId);
1489 
1490     return Response::RespOk;
1491 }
1492 
1493 Response::ResponseCode
cmdSetCardAttr(const Command_SetCardAttr & cmd,ResponseContainer &,GameEventStorage & ges)1494 Server_Player::cmdSetCardAttr(const Command_SetCardAttr &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1495 {
1496     if (spectator) {
1497         return Response::RespFunctionNotAllowed;
1498     }
1499 
1500     if (!game->getGameStarted()) {
1501         return Response::RespGameNotStarted;
1502     }
1503     if (conceded) {
1504         return Response::RespContextError;
1505     }
1506 
1507     return setCardAttrHelper(ges, playerId, QString::fromStdString(cmd.zone()), cmd.card_id(), cmd.attribute(),
1508                              QString::fromStdString(cmd.attr_value()));
1509 }
1510 
1511 Response::ResponseCode
cmdSetCardCounter(const Command_SetCardCounter & cmd,ResponseContainer &,GameEventStorage & ges)1512 Server_Player::cmdSetCardCounter(const Command_SetCardCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1513 {
1514     if (spectator) {
1515         return Response::RespFunctionNotAllowed;
1516     }
1517 
1518     if (!game->getGameStarted()) {
1519         return Response::RespGameNotStarted;
1520     }
1521     if (conceded) {
1522         return Response::RespContextError;
1523     }
1524 
1525     Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone()));
1526     if (!zone) {
1527         return Response::RespNameNotFound;
1528     }
1529     if (!zone->hasCoords()) {
1530         return Response::RespContextError;
1531     }
1532 
1533     Server_Card *card = zone->getCard(cmd.card_id());
1534     if (!card) {
1535         return Response::RespNameNotFound;
1536     }
1537 
1538     card->setCounter(cmd.counter_id(), cmd.counter_value());
1539 
1540     Event_SetCardCounter event;
1541     event.set_zone_name(zone->getName().toStdString());
1542     event.set_card_id(card->getId());
1543     event.set_counter_id(cmd.counter_id());
1544     event.set_counter_value(cmd.counter_value());
1545     ges.enqueueGameEvent(event, playerId);
1546 
1547     return Response::RespOk;
1548 }
1549 
1550 Response::ResponseCode
cmdIncCardCounter(const Command_IncCardCounter & cmd,ResponseContainer &,GameEventStorage & ges)1551 Server_Player::cmdIncCardCounter(const Command_IncCardCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1552 {
1553     if (spectator) {
1554         return Response::RespFunctionNotAllowed;
1555     }
1556 
1557     if (!game->getGameStarted()) {
1558         return Response::RespGameNotStarted;
1559     }
1560     if (conceded) {
1561         return Response::RespContextError;
1562     }
1563 
1564     Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone()));
1565     if (!zone) {
1566         return Response::RespNameNotFound;
1567     }
1568     if (!zone->hasCoords()) {
1569         return Response::RespContextError;
1570     }
1571 
1572     Server_Card *card = zone->getCard(cmd.card_id());
1573     if (!card) {
1574         return Response::RespNameNotFound;
1575     }
1576 
1577     int newValue = card->getCounter(cmd.counter_id()) + cmd.counter_delta();
1578     card->setCounter(cmd.counter_id(), newValue);
1579 
1580     Event_SetCardCounter event;
1581     event.set_zone_name(zone->getName().toStdString());
1582     event.set_card_id(card->getId());
1583     event.set_counter_id(cmd.counter_id());
1584     event.set_counter_value(newValue);
1585     ges.enqueueGameEvent(event, playerId);
1586 
1587     return Response::RespOk;
1588 }
1589 
1590 Response::ResponseCode
cmdIncCounter(const Command_IncCounter & cmd,ResponseContainer &,GameEventStorage & ges)1591 Server_Player::cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1592 {
1593     if (spectator) {
1594         return Response::RespFunctionNotAllowed;
1595     }
1596 
1597     if (!game->getGameStarted()) {
1598         return Response::RespGameNotStarted;
1599     }
1600     if (conceded) {
1601         return Response::RespContextError;
1602     }
1603 
1604     Server_Counter *c = counters.value(cmd.counter_id(), 0);
1605     if (!c) {
1606         return Response::RespNameNotFound;
1607     }
1608 
1609     c->setCount(c->getCount() + cmd.delta());
1610 
1611     Event_SetCounter event;
1612     event.set_counter_id(c->getId());
1613     event.set_value(c->getCount());
1614     ges.enqueueGameEvent(event, playerId);
1615 
1616     return Response::RespOk;
1617 }
1618 
1619 Response::ResponseCode
cmdCreateCounter(const Command_CreateCounter & cmd,ResponseContainer &,GameEventStorage & ges)1620 Server_Player::cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1621 {
1622     if (spectator) {
1623         return Response::RespFunctionNotAllowed;
1624     }
1625 
1626     if (!game->getGameStarted()) {
1627         return Response::RespGameNotStarted;
1628     }
1629     if (conceded) {
1630         return Response::RespContextError;
1631     }
1632 
1633     auto *c = new Server_Counter(newCounterId(), QString::fromStdString(cmd.counter_name()), cmd.counter_color(),
1634                                  cmd.radius(), cmd.value());
1635     addCounter(c);
1636 
1637     Event_CreateCounter event;
1638     ServerInfo_Counter *counterInfo = event.mutable_counter_info();
1639     counterInfo->set_id(c->getId());
1640     counterInfo->set_name(c->getName().toStdString());
1641     counterInfo->mutable_counter_color()->CopyFrom(cmd.counter_color());
1642     counterInfo->set_radius(c->getRadius());
1643     counterInfo->set_count(c->getCount());
1644     ges.enqueueGameEvent(event, playerId);
1645 
1646     return Response::RespOk;
1647 }
1648 
1649 Response::ResponseCode
cmdSetCounter(const Command_SetCounter & cmd,ResponseContainer &,GameEventStorage & ges)1650 Server_Player::cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1651 {
1652     if (spectator) {
1653         return Response::RespFunctionNotAllowed;
1654     }
1655 
1656     if (!game->getGameStarted()) {
1657         return Response::RespGameNotStarted;
1658     }
1659     if (conceded) {
1660         return Response::RespContextError;
1661     }
1662 
1663     Server_Counter *c = counters.value(cmd.counter_id(), 0);
1664     if (!c) {
1665         return Response::RespNameNotFound;
1666     }
1667 
1668     c->setCount(cmd.value());
1669 
1670     Event_SetCounter event;
1671     event.set_counter_id(c->getId());
1672     event.set_value(c->getCount());
1673     ges.enqueueGameEvent(event, playerId);
1674 
1675     return Response::RespOk;
1676 }
1677 
1678 Response::ResponseCode
cmdDelCounter(const Command_DelCounter & cmd,ResponseContainer &,GameEventStorage & ges)1679 Server_Player::cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1680 {
1681     if (spectator) {
1682         return Response::RespFunctionNotAllowed;
1683     }
1684 
1685     if (!game->getGameStarted()) {
1686         return Response::RespGameNotStarted;
1687     }
1688     if (conceded) {
1689         return Response::RespContextError;
1690     }
1691 
1692     Server_Counter *counter = counters.value(cmd.counter_id(), 0);
1693     if (!counter) {
1694         return Response::RespNameNotFound;
1695     }
1696     counters.remove(cmd.counter_id());
1697     delete counter;
1698 
1699     Event_DelCounter event;
1700     event.set_counter_id(cmd.counter_id());
1701     ges.enqueueGameEvent(event, playerId);
1702 
1703     return Response::RespOk;
1704 }
1705 
1706 Response::ResponseCode
cmdNextTurn(const Command_NextTurn &,ResponseContainer &,GameEventStorage &)1707 Server_Player::cmdNextTurn(const Command_NextTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/)
1708 {
1709     if (!game->getGameStarted()) {
1710         return Response::RespGameNotStarted;
1711     }
1712 
1713     if (!judge) {
1714         if (spectator) {
1715             return Response::RespFunctionNotAllowed;
1716         }
1717 
1718         if (conceded) {
1719             return Response::RespContextError;
1720         }
1721     }
1722 
1723     game->nextTurn();
1724     return Response::RespOk;
1725 }
1726 
cmdSetActivePhase(const Command_SetActivePhase & cmd,ResponseContainer &,GameEventStorage &)1727 Response::ResponseCode Server_Player::cmdSetActivePhase(const Command_SetActivePhase &cmd,
1728                                                         ResponseContainer & /*rc*/,
1729                                                         GameEventStorage & /*ges*/)
1730 {
1731     if (!game->getGameStarted()) {
1732         return Response::RespGameNotStarted;
1733     }
1734 
1735     if (!judge) {
1736         if (spectator) {
1737             return Response::RespFunctionNotAllowed;
1738         }
1739 
1740         if (conceded) {
1741             return Response::RespContextError;
1742         }
1743 
1744         if (game->getActivePlayer() != playerId) {
1745             return Response::RespContextError;
1746         }
1747     }
1748 
1749     game->setActivePhase(cmd.phase());
1750 
1751     return Response::RespOk;
1752 }
1753 
1754 Response::ResponseCode
cmdDumpZone(const Command_DumpZone & cmd,ResponseContainer & rc,GameEventStorage & ges)1755 Server_Player::cmdDumpZone(const Command_DumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges)
1756 {
1757     if (!game->getGameStarted()) {
1758         return Response::RespGameNotStarted;
1759     }
1760 
1761     Server_Player *otherPlayer = game->getPlayers().value(cmd.player_id());
1762     if (!otherPlayer) {
1763         return Response::RespNameNotFound;
1764     }
1765     Server_CardZone *zone = otherPlayer->getZones().value(QString::fromStdString(cmd.zone_name()));
1766     if (!zone) {
1767         return Response::RespNameNotFound;
1768     }
1769     if (!((zone->getType() == ServerInfo_Zone::PublicZone) || (this == otherPlayer))) {
1770         return Response::RespContextError;
1771     }
1772 
1773     int numberCards = cmd.number_cards();
1774     const QList<Server_Card *> &cards = zone->getCards();
1775 
1776     auto *re = new Response_DumpZone;
1777     ServerInfo_Zone *zoneInfo = re->mutable_zone_info();
1778     zoneInfo->set_name(zone->getName().toStdString());
1779     zoneInfo->set_type(zone->getType());
1780     zoneInfo->set_with_coords(zone->hasCoords());
1781     zoneInfo->set_card_count(numberCards < cards.size() ? cards.size() : numberCards);
1782 
1783     for (int i = 0; (i < cards.size()) && (i < numberCards || numberCards == -1); ++i) {
1784         Server_Card *card = cards[i];
1785         QString displayedName = card->getFaceDown() ? QString() : card->getName();
1786         ServerInfo_Card *cardInfo = zoneInfo->add_card_list();
1787         cardInfo->set_name(displayedName.toStdString());
1788         if (zone->getType() == ServerInfo_Zone::HiddenZone) {
1789             cardInfo->set_id(i);
1790         } else {
1791             cardInfo->set_id(card->getId());
1792             cardInfo->set_x(card->getX());
1793             cardInfo->set_y(card->getY());
1794             cardInfo->set_face_down(card->getFaceDown());
1795             cardInfo->set_tapped(card->getTapped());
1796             cardInfo->set_attacking(card->getAttacking());
1797             cardInfo->set_color(card->getColor().toStdString());
1798             cardInfo->set_pt(card->getPT().toStdString());
1799             cardInfo->set_annotation(card->getAnnotation().toStdString());
1800             cardInfo->set_destroy_on_zone_change(card->getDestroyOnZoneChange());
1801             cardInfo->set_doesnt_untap(card->getDoesntUntap());
1802 
1803             QMapIterator<int, int> cardCounterIterator(card->getCounters());
1804             while (cardCounterIterator.hasNext()) {
1805                 cardCounterIterator.next();
1806                 ServerInfo_CardCounter *counterInfo = cardInfo->add_counter_list();
1807                 counterInfo->set_id(cardCounterIterator.key());
1808                 counterInfo->set_value(cardCounterIterator.value());
1809             }
1810 
1811             if (card->getParentCard()) {
1812                 cardInfo->set_attach_player_id(card->getParentCard()->getZone()->getPlayer()->getPlayerId());
1813                 cardInfo->set_attach_zone(card->getParentCard()->getZone()->getName().toStdString());
1814                 cardInfo->set_attach_card_id(card->getParentCard()->getId());
1815             }
1816         }
1817     }
1818     if (zone->getType() == ServerInfo_Zone::HiddenZone) {
1819         zone->setCardsBeingLookedAt(numberCards);
1820 
1821         Event_DumpZone event;
1822         event.set_zone_owner_id(otherPlayer->getPlayerId());
1823         event.set_zone_name(zone->getName().toStdString());
1824         event.set_number_cards(numberCards);
1825         ges.enqueueGameEvent(event, playerId);
1826     }
1827     rc.setResponseExtension(re);
1828     return Response::RespOk;
1829 }
1830 
1831 Response::ResponseCode
cmdStopDumpZone(const Command_StopDumpZone & cmd,ResponseContainer &,GameEventStorage & ges)1832 Server_Player::cmdStopDumpZone(const Command_StopDumpZone &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1833 {
1834     if (!game->getGameStarted()) {
1835         return Response::RespGameNotStarted;
1836     }
1837     if (conceded) {
1838         return Response::RespContextError;
1839     }
1840 
1841     Server_Player *otherPlayer = game->getPlayers().value(cmd.player_id());
1842     if (!otherPlayer) {
1843         return Response::RespNameNotFound;
1844     }
1845     Server_CardZone *zone = otherPlayer->getZones().value(QString::fromStdString(cmd.zone_name()));
1846     if (!zone) {
1847         return Response::RespNameNotFound;
1848     }
1849 
1850     if (zone->getType() == ServerInfo_Zone::HiddenZone) {
1851         zone->setCardsBeingLookedAt(0);
1852 
1853         Event_StopDumpZone event;
1854         event.set_zone_owner_id(cmd.player_id());
1855         event.set_zone_name(zone->getName().toStdString());
1856         ges.enqueueGameEvent(event, playerId);
1857     }
1858     return Response::RespOk;
1859 }
1860 
1861 Response::ResponseCode
cmdRevealCards(const Command_RevealCards & cmd,ResponseContainer &,GameEventStorage & ges)1862 Server_Player::cmdRevealCards(const Command_RevealCards &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
1863 {
1864     if (spectator) {
1865         return Response::RespFunctionNotAllowed;
1866     }
1867 
1868     if (!game->getGameStarted()) {
1869         return Response::RespGameNotStarted;
1870     }
1871     if (conceded) {
1872         return Response::RespContextError;
1873     }
1874 
1875     if (cmd.has_player_id()) {
1876         Server_Player *otherPlayer = game->getPlayers().value(cmd.player_id());
1877         if (!otherPlayer)
1878             return Response::RespNameNotFound;
1879     }
1880     Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone_name()));
1881     if (!zone) {
1882         return Response::RespNameNotFound;
1883     }
1884 
1885     QList<Server_Card *> cardsToReveal;
1886     if (cmd.top_cards() != -1) {
1887         for (int i = 0; i < cmd.top_cards(); i++) {
1888             Server_Card *card = zone->getCard(i);
1889             if (!card) {
1890                 return Response::RespNameNotFound;
1891             }
1892             cardsToReveal.append(card);
1893         }
1894     } else if (!cmd.has_card_id()) {
1895         cardsToReveal = zone->getCards();
1896     } else if (cmd.card_id() == -2) {
1897         if (zone->getCards().isEmpty()) {
1898             return Response::RespContextError;
1899         }
1900         cardsToReveal.append(zone->getCards().at(rng->rand(0, zone->getCards().size() - 1)));
1901     } else {
1902         Server_Card *card = zone->getCard(cmd.card_id());
1903         if (!card) {
1904             return Response::RespNameNotFound;
1905         }
1906         cardsToReveal.append(card);
1907     }
1908 
1909     Event_RevealCards eventOthers;
1910     eventOthers.set_grant_write_access(cmd.grant_write_access());
1911     eventOthers.set_zone_name(zone->getName().toStdString());
1912     eventOthers.set_number_of_cards(cardsToReveal.size());
1913     if (cmd.has_card_id()) {
1914         eventOthers.set_card_id(cmd.card_id());
1915     }
1916     if (cmd.has_player_id()) {
1917         eventOthers.set_other_player_id(cmd.player_id());
1918     }
1919 
1920     Event_RevealCards eventPrivate(eventOthers);
1921 
1922     for (auto card : cardsToReveal) {
1923         ServerInfo_Card *cardInfo = eventPrivate.add_cards();
1924 
1925         cardInfo->set_id(card->getId());
1926         cardInfo->set_name(card->getName().toStdString());
1927         cardInfo->set_x(card->getX());
1928         cardInfo->set_y(card->getY());
1929         cardInfo->set_face_down(card->getFaceDown());
1930         cardInfo->set_tapped(card->getTapped());
1931         cardInfo->set_attacking(card->getAttacking());
1932         cardInfo->set_color(card->getColor().toStdString());
1933         cardInfo->set_pt(card->getPT().toStdString());
1934         cardInfo->set_annotation(card->getAnnotation().toStdString());
1935         cardInfo->set_destroy_on_zone_change(card->getDestroyOnZoneChange());
1936         cardInfo->set_doesnt_untap(card->getDoesntUntap());
1937 
1938         QMapIterator<int, int> cardCounterIterator(card->getCounters());
1939         while (cardCounterIterator.hasNext()) {
1940             cardCounterIterator.next();
1941             ServerInfo_CardCounter *counterInfo = cardInfo->add_counter_list();
1942             counterInfo->set_id(cardCounterIterator.key());
1943             counterInfo->set_value(cardCounterIterator.value());
1944         }
1945 
1946         if (card->getParentCard()) {
1947             cardInfo->set_attach_player_id(card->getParentCard()->getZone()->getPlayer()->getPlayerId());
1948             cardInfo->set_attach_zone(card->getParentCard()->getZone()->getName().toStdString());
1949             cardInfo->set_attach_card_id(card->getParentCard()->getId());
1950         }
1951     }
1952 
1953     if (cmd.has_player_id()) {
1954         if (cmd.grant_write_access()) {
1955             zone->addWritePermission(cmd.player_id());
1956         }
1957 
1958         ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, cmd.player_id());
1959         ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
1960     } else {
1961         if (cmd.grant_write_access()) {
1962             const QList<int> &playerIds = game->getPlayers().keys();
1963             for (int playerId : playerIds) {
1964                 zone->addWritePermission(playerId);
1965             }
1966         }
1967 
1968         ges.enqueueGameEvent(eventPrivate, playerId);
1969     }
1970 
1971     return Response::RespOk;
1972 }
1973 
cmdChangeZoneProperties(const Command_ChangeZoneProperties & cmd,ResponseContainer &,GameEventStorage & ges)1974 Response::ResponseCode Server_Player::cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd,
1975                                                               ResponseContainer & /* rc */,
1976                                                               GameEventStorage &ges)
1977 {
1978     Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone_name()));
1979     if (!zone) {
1980         return Response::RespNameNotFound;
1981     }
1982 
1983     Event_ChangeZoneProperties event;
1984     event.set_zone_name(cmd.zone_name());
1985 
1986     if (cmd.has_always_reveal_top_card()) {
1987         if (zone->getAlwaysRevealTopCard() == cmd.always_reveal_top_card()) {
1988             return Response::RespContextError;
1989         }
1990         zone->setAlwaysRevealTopCard(cmd.always_reveal_top_card());
1991         event.set_always_reveal_top_card(cmd.always_reveal_top_card());
1992 
1993         ges.enqueueGameEvent(event, playerId);
1994 
1995         if (!zone->getCards().isEmpty() && cmd.always_reveal_top_card()) {
1996             Event_RevealCards revealEvent;
1997             revealEvent.set_zone_name(zone->getName().toStdString());
1998             revealEvent.set_card_id(0);
1999             zone->getCards().first()->getInfo(revealEvent.add_cards());
2000 
2001             ges.enqueueGameEvent(revealEvent, playerId);
2002         }
2003         return Response::RespOk;
2004     } else {
2005         return Response::RespContextError;
2006     }
2007 }
2008 
2009 Response::ResponseCode
cmdReverseTurn(const Command_ReverseTurn &,ResponseContainer &,GameEventStorage & ges)2010 Server_Player::cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges)
2011 {
2012     if (spectator) {
2013         return Response::RespFunctionNotAllowed;
2014     }
2015 
2016     if (!game->getGameStarted()) {
2017         return Response::RespGameNotStarted;
2018     }
2019 
2020     if (conceded) {
2021         return Response::RespContextError;
2022     }
2023 
2024     bool reversedTurn = game->reverseTurnOrder();
2025 
2026     Event_ReverseTurn event;
2027     event.set_reversed(reversedTurn);
2028     ges.enqueueGameEvent(event, playerId);
2029 
2030     return Response::RespOk;
2031 }
2032 
2033 Response::ResponseCode
processGameCommand(const GameCommand & command,ResponseContainer & rc,GameEventStorage & ges)2034 Server_Player::processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges)
2035 {
2036     switch ((GameCommand::GameCommandType)getPbExtension(command)) {
2037         case GameCommand::KICK_FROM_GAME:
2038             return cmdKickFromGame(command.GetExtension(Command_KickFromGame::ext), rc, ges);
2039             break;
2040         case GameCommand::LEAVE_GAME:
2041             return cmdLeaveGame(command.GetExtension(Command_LeaveGame::ext), rc, ges);
2042             break;
2043         case GameCommand::GAME_SAY:
2044             return cmdGameSay(command.GetExtension(Command_GameSay::ext), rc, ges);
2045             break;
2046         case GameCommand::SHUFFLE:
2047             return cmdShuffle(command.GetExtension(Command_Shuffle::ext), rc, ges);
2048             break;
2049         case GameCommand::MULLIGAN:
2050             return cmdMulligan(command.GetExtension(Command_Mulligan::ext), rc, ges);
2051             break;
2052         case GameCommand::ROLL_DIE:
2053             return cmdRollDie(command.GetExtension(Command_RollDie::ext), rc, ges);
2054             break;
2055         case GameCommand::DRAW_CARDS:
2056             return cmdDrawCards(command.GetExtension(Command_DrawCards::ext), rc, ges);
2057             break;
2058         case GameCommand::UNDO_DRAW:
2059             return cmdUndoDraw(command.GetExtension(Command_UndoDraw::ext), rc, ges);
2060             break;
2061         case GameCommand::FLIP_CARD:
2062             return cmdFlipCard(command.GetExtension(Command_FlipCard::ext), rc, ges);
2063             break;
2064         case GameCommand::ATTACH_CARD:
2065             return cmdAttachCard(command.GetExtension(Command_AttachCard::ext), rc, ges);
2066             break;
2067         case GameCommand::CREATE_TOKEN:
2068             return cmdCreateToken(command.GetExtension(Command_CreateToken::ext), rc, ges);
2069             break;
2070         case GameCommand::CREATE_ARROW:
2071             return cmdCreateArrow(command.GetExtension(Command_CreateArrow::ext), rc, ges);
2072             break;
2073         case GameCommand::DELETE_ARROW:
2074             return cmdDeleteArrow(command.GetExtension(Command_DeleteArrow::ext), rc, ges);
2075             break;
2076         case GameCommand::SET_CARD_ATTR:
2077             return cmdSetCardAttr(command.GetExtension(Command_SetCardAttr::ext), rc, ges);
2078             break;
2079         case GameCommand::SET_CARD_COUNTER:
2080             return cmdSetCardCounter(command.GetExtension(Command_SetCardCounter::ext), rc, ges);
2081             break;
2082         case GameCommand::INC_CARD_COUNTER:
2083             return cmdIncCardCounter(command.GetExtension(Command_IncCardCounter::ext), rc, ges);
2084             break;
2085         case GameCommand::READY_START:
2086             return cmdReadyStart(command.GetExtension(Command_ReadyStart::ext), rc, ges);
2087             break;
2088         case GameCommand::CONCEDE:
2089             return cmdConcede(command.GetExtension(Command_Concede::ext), rc, ges);
2090             break;
2091         case GameCommand::INC_COUNTER:
2092             return cmdIncCounter(command.GetExtension(Command_IncCounter::ext), rc, ges);
2093             break;
2094         case GameCommand::CREATE_COUNTER:
2095             return cmdCreateCounter(command.GetExtension(Command_CreateCounter::ext), rc, ges);
2096             break;
2097         case GameCommand::SET_COUNTER:
2098             return cmdSetCounter(command.GetExtension(Command_SetCounter::ext), rc, ges);
2099             break;
2100         case GameCommand::DEL_COUNTER:
2101             return cmdDelCounter(command.GetExtension(Command_DelCounter::ext), rc, ges);
2102             break;
2103         case GameCommand::NEXT_TURN:
2104             return cmdNextTurn(command.GetExtension(Command_NextTurn::ext), rc, ges);
2105             break;
2106         case GameCommand::SET_ACTIVE_PHASE:
2107             return cmdSetActivePhase(command.GetExtension(Command_SetActivePhase::ext), rc, ges);
2108             break;
2109         case GameCommand::DUMP_ZONE:
2110             return cmdDumpZone(command.GetExtension(Command_DumpZone::ext), rc, ges);
2111             break;
2112         case GameCommand::STOP_DUMP_ZONE:
2113             return cmdStopDumpZone(command.GetExtension(Command_StopDumpZone::ext), rc, ges);
2114             break;
2115         case GameCommand::REVEAL_CARDS:
2116             return cmdRevealCards(command.GetExtension(Command_RevealCards::ext), rc, ges);
2117             break;
2118         case GameCommand::MOVE_CARD:
2119             return cmdMoveCard(command.GetExtension(Command_MoveCard::ext), rc, ges);
2120             break;
2121         case GameCommand::SET_SIDEBOARD_PLAN:
2122             return cmdSetSideboardPlan(command.GetExtension(Command_SetSideboardPlan::ext), rc, ges);
2123             break;
2124         case GameCommand::DECK_SELECT:
2125             return cmdDeckSelect(command.GetExtension(Command_DeckSelect::ext), rc, ges);
2126             break;
2127         case GameCommand::SET_SIDEBOARD_LOCK:
2128             return cmdSetSideboardLock(command.GetExtension(Command_SetSideboardLock::ext), rc, ges);
2129             break;
2130         case GameCommand::CHANGE_ZONE_PROPERTIES:
2131             return cmdChangeZoneProperties(command.GetExtension(Command_ChangeZoneProperties::ext), rc, ges);
2132             break;
2133         case GameCommand::UNCONCEDE:
2134             return cmdUnconcede(command.GetExtension(Command_Unconcede::ext), rc, ges);
2135             break;
2136         case GameCommand::JUDGE:
2137             return cmdJudge(command.GetExtension(Command_Judge::ext), rc, ges);
2138             break;
2139         case GameCommand::REVERSE_TURN:
2140             return cmdReverseTurn(command.GetExtension(Command_ReverseTurn::ext), rc, ges);
2141             break;
2142         default:
2143             return Response::RespInvalidCommand;
2144     }
2145 }
2146 
sendGameEvent(const GameEventContainer & cont)2147 void Server_Player::sendGameEvent(const GameEventContainer &cont)
2148 {
2149     QMutexLocker locker(&playerMutex);
2150 
2151     if (userInterface) {
2152         userInterface->sendProtocolItem(cont);
2153     }
2154 }
2155 
setUserInterface(Server_AbstractUserInterface * _userInterface)2156 void Server_Player::setUserInterface(Server_AbstractUserInterface *_userInterface)
2157 {
2158     playerMutex.lock();
2159     userInterface = _userInterface;
2160     playerMutex.unlock();
2161 
2162     pingTime = _userInterface ? 0 : -1;
2163 
2164     Event_PlayerPropertiesChanged event;
2165     event.mutable_player_properties()->set_ping_seconds(pingTime);
2166 
2167     GameEventStorage ges;
2168     ges.setGameEventContext(Context_ConnectionStateChanged());
2169     ges.enqueueGameEvent(event, playerId);
2170     ges.sendToGame(game);
2171 }
2172 
disconnectClient()2173 void Server_Player::disconnectClient()
2174 {
2175     if (!(userInfo->user_level() & ServerInfo_User::IsRegistered) || spectator) {
2176         game->removePlayer(this, Event_Leave::USER_DISCONNECTED);
2177     } else {
2178         setUserInterface(nullptr);
2179     }
2180 }
2181 
getInfo(ServerInfo_Player * info,Server_Player * playerWhosAsking,bool omniscient,bool withUserInfo)2182 void Server_Player::getInfo(ServerInfo_Player *info,
2183                             Server_Player *playerWhosAsking,
2184                             bool omniscient,
2185                             bool withUserInfo)
2186 {
2187     getProperties(*info->mutable_properties(), withUserInfo);
2188     if (playerWhosAsking == this) {
2189         if (deck) {
2190             info->set_deck_list(deck->writeToString_Native().toStdString());
2191         }
2192     }
2193 
2194     for (Server_Arrow *arrow : arrows) {
2195         arrow->getInfo(info->add_arrow_list());
2196     }
2197 
2198     for (Server_Counter *counter : counters) {
2199         counter->getInfo(info->add_counter_list());
2200     }
2201 
2202     for (Server_CardZone *zone : zones) {
2203         zone->getInfo(info->add_zone_list(), playerWhosAsking, omniscient);
2204     }
2205 }
2206