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