mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-12 09:04:53 -07:00
* move common server files * update includes with move * create participant, move code * fix linker errors * fix regressions * mark function as override to make clang happy * split out spectator to new file * forgot to add to cmakelists * autocompleter picking wrong casing for var name * clean up forwards declarations in player * fix includes in game
This commit is contained in:
parent
17dcaf9afa
commit
f0c3860032
45 changed files with 1283 additions and 866 deletions
612
common/server/game/server_abstract_participant.cpp
Normal file
612
common/server/game/server_abstract_participant.cpp
Normal file
|
|
@ -0,0 +1,612 @@
|
|||
#include "server_abstract_participant.h"
|
||||
|
||||
#include "../../color.h"
|
||||
#include "../../deck_list.h"
|
||||
#include "../../deck_list_card_node.h"
|
||||
#include "../../get_pb_extension.h"
|
||||
#include "../../rng_abstract.h"
|
||||
#include "../../trice_limits.h"
|
||||
#include "../server.h"
|
||||
#include "../server_abstractuserinterface.h"
|
||||
#include "../server_database_interface.h"
|
||||
#include "../server_room.h"
|
||||
#include "pb/command_attach_card.pb.h"
|
||||
#include "pb/command_change_zone_properties.pb.h"
|
||||
#include "pb/command_concede.pb.h"
|
||||
#include "pb/command_create_arrow.pb.h"
|
||||
#include "pb/command_create_counter.pb.h"
|
||||
#include "pb/command_create_token.pb.h"
|
||||
#include "pb/command_deck_select.pb.h"
|
||||
#include "pb/command_del_counter.pb.h"
|
||||
#include "pb/command_delete_arrow.pb.h"
|
||||
#include "pb/command_draw_cards.pb.h"
|
||||
#include "pb/command_dump_zone.pb.h"
|
||||
#include "pb/command_flip_card.pb.h"
|
||||
#include "pb/command_game_say.pb.h"
|
||||
#include "pb/command_inc_card_counter.pb.h"
|
||||
#include "pb/command_inc_counter.pb.h"
|
||||
#include "pb/command_kick_from_game.pb.h"
|
||||
#include "pb/command_leave_game.pb.h"
|
||||
#include "pb/command_move_card.pb.h"
|
||||
#include "pb/command_mulligan.pb.h"
|
||||
#include "pb/command_next_turn.pb.h"
|
||||
#include "pb/command_ready_start.pb.h"
|
||||
#include "pb/command_reveal_cards.pb.h"
|
||||
#include "pb/command_reverse_turn.pb.h"
|
||||
#include "pb/command_roll_die.pb.h"
|
||||
#include "pb/command_set_active_phase.pb.h"
|
||||
#include "pb/command_set_card_attr.pb.h"
|
||||
#include "pb/command_set_card_counter.pb.h"
|
||||
#include "pb/command_set_counter.pb.h"
|
||||
#include "pb/command_set_sideboard_lock.pb.h"
|
||||
#include "pb/command_set_sideboard_plan.pb.h"
|
||||
#include "pb/command_shuffle.pb.h"
|
||||
#include "pb/command_undo_draw.pb.h"
|
||||
#include "pb/context_concede.pb.h"
|
||||
#include "pb/context_connection_state_changed.pb.h"
|
||||
#include "pb/context_deck_select.pb.h"
|
||||
#include "pb/context_move_card.pb.h"
|
||||
#include "pb/context_mulligan.pb.h"
|
||||
#include "pb/context_ready_start.pb.h"
|
||||
#include "pb/context_set_sideboard_lock.pb.h"
|
||||
#include "pb/context_undo_draw.pb.h"
|
||||
#include "pb/event_attach_card.pb.h"
|
||||
#include "pb/event_change_zone_properties.pb.h"
|
||||
#include "pb/event_create_arrow.pb.h"
|
||||
#include "pb/event_create_counter.pb.h"
|
||||
#include "pb/event_create_token.pb.h"
|
||||
#include "pb/event_del_counter.pb.h"
|
||||
#include "pb/event_delete_arrow.pb.h"
|
||||
#include "pb/event_destroy_card.pb.h"
|
||||
#include "pb/event_draw_cards.pb.h"
|
||||
#include "pb/event_dump_zone.pb.h"
|
||||
#include "pb/event_flip_card.pb.h"
|
||||
#include "pb/event_game_say.pb.h"
|
||||
#include "pb/event_move_card.pb.h"
|
||||
#include "pb/event_player_properties_changed.pb.h"
|
||||
#include "pb/event_reveal_cards.pb.h"
|
||||
#include "pb/event_reverse_turn.pb.h"
|
||||
#include "pb/event_roll_die.pb.h"
|
||||
#include "pb/event_set_card_attr.pb.h"
|
||||
#include "pb/event_set_card_counter.pb.h"
|
||||
#include "pb/event_set_counter.pb.h"
|
||||
#include "pb/event_shuffle.pb.h"
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/response_deck_download.pb.h"
|
||||
#include "pb/response_dump_zone.pb.h"
|
||||
#include "pb/serverinfo_player.pb.h"
|
||||
#include "pb/serverinfo_user.pb.h"
|
||||
#include "server_arrow.h"
|
||||
#include "server_card.h"
|
||||
#include "server_cardzone.h"
|
||||
#include "server_counter.h"
|
||||
#include "server_game.h"
|
||||
#include "server_player.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
#include <algorithm>
|
||||
|
||||
Server_AbstractParticipant::Server_AbstractParticipant(Server_Game *_game,
|
||||
int _playerId,
|
||||
const ServerInfo_User &_userInfo,
|
||||
bool _judge,
|
||||
Server_AbstractUserInterface *_userInterface)
|
||||
: ServerInfo_User_Container(_userInfo), game(_game), userInterface(_userInterface), pingTime(0),
|
||||
playerId(_playerId), judge(_judge)
|
||||
{
|
||||
}
|
||||
|
||||
Server_AbstractParticipant::~Server_AbstractParticipant() = default;
|
||||
|
||||
void Server_AbstractParticipant::removeFromGame()
|
||||
{
|
||||
QMutexLocker locker(&playerMutex);
|
||||
if (userInterface) {
|
||||
userInterface->playerRemovedFromGame(game);
|
||||
}
|
||||
}
|
||||
|
||||
bool Server_AbstractParticipant::updatePingTime() // returns true if ping time changed
|
||||
{
|
||||
QMutexLocker locker(&playerMutex);
|
||||
|
||||
int oldPingTime = pingTime;
|
||||
if (userInterface) {
|
||||
pingTime = userInterface->getLastCommandTime();
|
||||
} else {
|
||||
pingTime = -1;
|
||||
}
|
||||
|
||||
return pingTime != oldPingTime;
|
||||
}
|
||||
|
||||
void Server_AbstractParticipant::getProperties(ServerInfo_PlayerProperties &result, bool withUserInfo)
|
||||
{
|
||||
result.set_player_id(playerId);
|
||||
if (withUserInfo) {
|
||||
copyUserInfo(*(result.mutable_user_info()), true);
|
||||
}
|
||||
result.set_spectator(spectator);
|
||||
result.set_judge(judge);
|
||||
result.set_ping_seconds(pingTime);
|
||||
getPlayerProperties(result);
|
||||
}
|
||||
|
||||
void Server_AbstractParticipant::getPlayerProperties(ServerInfo_PlayerProperties & /*result*/)
|
||||
{
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdLeaveGame(const Command_LeaveGame & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
game->removeParticipant(this, Event_Leave::USER_LEFT);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdKickFromGame(const Command_KickFromGame &cmd,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
if ((game->getHostId() != playerId) && !(userInfo->user_level() & ServerInfo_User::IsModerator)) {
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
if (!game->kickParticipant(cmd.player_id())) {
|
||||
return Response::RespNameNotFound;
|
||||
}
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdDeckSelect(const Command_DeckSelect & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdSetSideboardPlan(const Command_SetSideboardPlan & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdSetSideboardLock(const Command_SetSideboardLock & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdConcede(const Command_Concede & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdUnconcede(const Command_Unconcede & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode
|
||||
Server_AbstractParticipant::cmdJudge(const Command_Judge &cmd, ResponseContainer &rc, GameEventStorage &ges)
|
||||
{
|
||||
if (!judge) {
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Server_Player *player = this->game->getPlayer(cmd.target_id());
|
||||
|
||||
ges.setForcedByJudge(playerId);
|
||||
if (player == nullptr) {
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cmd.game_command_size(); ++i) {
|
||||
player->processGameCommand(cmd.game_command(i), rc, ges);
|
||||
}
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdReadyStart(const Command_ReadyStart & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode
|
||||
Server_AbstractParticipant::cmdGameSay(const Command_GameSay &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
||||
{
|
||||
if (spectator) {
|
||||
/* Spectators can only talk if:
|
||||
* (a) the game creator allows it
|
||||
* (b) the spectator is a moderator/administrator
|
||||
* (c) the spectator is a judge
|
||||
*/
|
||||
bool isModOrJudge = (userInfo->user_level() & (ServerInfo_User::IsModerator | ServerInfo_User::IsJudge));
|
||||
if (!isModOrJudge && !game->getSpectatorsCanTalk()) {
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
}
|
||||
|
||||
if (!userInterface->addSaidMessageSize(static_cast<int>(cmd.message().size()))) {
|
||||
return Response::RespChatFlood;
|
||||
}
|
||||
Event_GameSay event;
|
||||
event.set_message(cmd.message());
|
||||
ges.enqueueGameEvent(event, playerId);
|
||||
|
||||
game->getRoom()->getServer()->getDatabaseInterface()->logMessage(
|
||||
userInfo->id(), QString::fromStdString(userInfo->name()), QString::fromStdString(userInfo->address()),
|
||||
textFromStdString(cmd.message()), Server_DatabaseInterface::MessageTargetGame, game->getGameId(),
|
||||
game->getDescription());
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdShuffle(const Command_Shuffle & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdMulligan(const Command_Mulligan & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdRollDie(const Command_RollDie & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/) const
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdDrawCards(const Command_DrawCards & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdUndoDraw(const Command_UndoDraw & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdMoveCard(const Command_MoveCard & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdFlipCard(const Command_FlipCard & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdAttachCard(const Command_AttachCard & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdCreateToken(const Command_CreateToken & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdCreateArrow(const Command_CreateArrow & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdDeleteArrow(const Command_DeleteArrow & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdSetCardAttr(const Command_SetCardAttr & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdSetCardCounter(const Command_SetCardCounter & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdIncCardCounter(const Command_IncCardCounter & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdIncCounter(const Command_IncCounter & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdCreateCounter(const Command_CreateCounter & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdSetCounter(const Command_SetCounter & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdDelCounter(const Command_DelCounter & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdNextTurn(const Command_NextTurn & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
if (!game->getGameStarted()) {
|
||||
return Response::RespGameNotStarted;
|
||||
}
|
||||
|
||||
if (!judge) {
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
game->nextTurn();
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdSetActivePhase(const Command_SetActivePhase &cmd,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
if (!game->getGameStarted()) {
|
||||
return Response::RespGameNotStarted;
|
||||
}
|
||||
|
||||
if (!judge) {
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
game->setActivePhase(cmd.phase());
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdDumpZone(const Command_DumpZone & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdRevealCards(const Command_RevealCards & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdChangeZoneProperties(const Command_ChangeZoneProperties & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdReverseTurn(const Command_ReverseTurn & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage &ges)
|
||||
{
|
||||
if (!judge) {
|
||||
if (spectator) {
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
if (!game->getGameStarted()) {
|
||||
return Response::RespGameNotStarted;
|
||||
}
|
||||
}
|
||||
|
||||
bool reversedTurn = game->reverseTurnOrder();
|
||||
|
||||
Event_ReverseTurn event;
|
||||
event.set_reversed(reversedTurn);
|
||||
ges.enqueueGameEvent(event, playerId);
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode
|
||||
Server_AbstractParticipant::processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges)
|
||||
{
|
||||
switch ((GameCommand::GameCommandType)getPbExtension(command)) {
|
||||
case GameCommand::KICK_FROM_GAME:
|
||||
return cmdKickFromGame(command.GetExtension(Command_KickFromGame::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::LEAVE_GAME:
|
||||
return cmdLeaveGame(command.GetExtension(Command_LeaveGame::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::GAME_SAY:
|
||||
return cmdGameSay(command.GetExtension(Command_GameSay::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::SHUFFLE:
|
||||
return cmdShuffle(command.GetExtension(Command_Shuffle::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::MULLIGAN:
|
||||
return cmdMulligan(command.GetExtension(Command_Mulligan::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::ROLL_DIE:
|
||||
return cmdRollDie(command.GetExtension(Command_RollDie::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::DRAW_CARDS:
|
||||
return cmdDrawCards(command.GetExtension(Command_DrawCards::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::UNDO_DRAW:
|
||||
return cmdUndoDraw(command.GetExtension(Command_UndoDraw::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::FLIP_CARD:
|
||||
return cmdFlipCard(command.GetExtension(Command_FlipCard::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::ATTACH_CARD:
|
||||
return cmdAttachCard(command.GetExtension(Command_AttachCard::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::CREATE_TOKEN:
|
||||
return cmdCreateToken(command.GetExtension(Command_CreateToken::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::CREATE_ARROW:
|
||||
return cmdCreateArrow(command.GetExtension(Command_CreateArrow::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::DELETE_ARROW:
|
||||
return cmdDeleteArrow(command.GetExtension(Command_DeleteArrow::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::SET_CARD_ATTR:
|
||||
return cmdSetCardAttr(command.GetExtension(Command_SetCardAttr::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::SET_CARD_COUNTER:
|
||||
return cmdSetCardCounter(command.GetExtension(Command_SetCardCounter::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::INC_CARD_COUNTER:
|
||||
return cmdIncCardCounter(command.GetExtension(Command_IncCardCounter::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::READY_START:
|
||||
return cmdReadyStart(command.GetExtension(Command_ReadyStart::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::CONCEDE:
|
||||
return cmdConcede(command.GetExtension(Command_Concede::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::INC_COUNTER:
|
||||
return cmdIncCounter(command.GetExtension(Command_IncCounter::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::CREATE_COUNTER:
|
||||
return cmdCreateCounter(command.GetExtension(Command_CreateCounter::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::SET_COUNTER:
|
||||
return cmdSetCounter(command.GetExtension(Command_SetCounter::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::DEL_COUNTER:
|
||||
return cmdDelCounter(command.GetExtension(Command_DelCounter::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::NEXT_TURN:
|
||||
return cmdNextTurn(command.GetExtension(Command_NextTurn::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::SET_ACTIVE_PHASE:
|
||||
return cmdSetActivePhase(command.GetExtension(Command_SetActivePhase::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::DUMP_ZONE:
|
||||
return cmdDumpZone(command.GetExtension(Command_DumpZone::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::REVEAL_CARDS:
|
||||
return cmdRevealCards(command.GetExtension(Command_RevealCards::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::MOVE_CARD:
|
||||
return cmdMoveCard(command.GetExtension(Command_MoveCard::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::SET_SIDEBOARD_PLAN:
|
||||
return cmdSetSideboardPlan(command.GetExtension(Command_SetSideboardPlan::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::DECK_SELECT:
|
||||
return cmdDeckSelect(command.GetExtension(Command_DeckSelect::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::SET_SIDEBOARD_LOCK:
|
||||
return cmdSetSideboardLock(command.GetExtension(Command_SetSideboardLock::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::CHANGE_ZONE_PROPERTIES:
|
||||
return cmdChangeZoneProperties(command.GetExtension(Command_ChangeZoneProperties::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::UNCONCEDE:
|
||||
return cmdUnconcede(command.GetExtension(Command_Unconcede::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::JUDGE:
|
||||
return cmdJudge(command.GetExtension(Command_Judge::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::REVERSE_TURN:
|
||||
return cmdReverseTurn(command.GetExtension(Command_ReverseTurn::ext), rc, ges);
|
||||
break;
|
||||
default:
|
||||
return Response::RespInvalidCommand;
|
||||
}
|
||||
}
|
||||
|
||||
void Server_AbstractParticipant::sendGameEvent(const GameEventContainer &cont)
|
||||
{
|
||||
QMutexLocker locker(&playerMutex);
|
||||
|
||||
if (userInterface) {
|
||||
userInterface->sendProtocolItem(cont);
|
||||
}
|
||||
}
|
||||
|
||||
void Server_AbstractParticipant::setUserInterface(Server_AbstractUserInterface *_userInterface)
|
||||
{
|
||||
playerMutex.lock();
|
||||
userInterface = _userInterface;
|
||||
playerMutex.unlock();
|
||||
|
||||
pingTime = _userInterface ? 0 : -1;
|
||||
|
||||
Event_PlayerPropertiesChanged event;
|
||||
event.mutable_player_properties()->set_ping_seconds(pingTime);
|
||||
|
||||
GameEventStorage ges;
|
||||
ges.setGameEventContext(Context_ConnectionStateChanged());
|
||||
ges.enqueueGameEvent(event, playerId);
|
||||
ges.sendToGame(game);
|
||||
}
|
||||
|
||||
void Server_AbstractParticipant::disconnectClient()
|
||||
{
|
||||
bool isRegistered = userInfo->user_level() & ServerInfo_User::IsRegistered;
|
||||
if (!isRegistered || spectator) {
|
||||
game->removeParticipant(this, Event_Leave::USER_DISCONNECTED);
|
||||
} else {
|
||||
setUserInterface(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Server_AbstractParticipant::getInfo(ServerInfo_Player *info,
|
||||
Server_AbstractParticipant * /*recipient*/,
|
||||
bool /* omniscient */,
|
||||
bool withUserInfo)
|
||||
{
|
||||
getProperties(*info->mutable_properties(), withUserInfo);
|
||||
}
|
||||
183
common/server/game/server_abstract_participant.h
Normal file
183
common/server/game/server_abstract_participant.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
#ifndef ABSTRACT_PARTICIPANT_H
|
||||
#define ABSTRACT_PARTICIPANT_H
|
||||
|
||||
#include "../../serverinfo_user_container.h"
|
||||
#include "pb/card_attributes.pb.h"
|
||||
#include "pb/response.pb.h"
|
||||
#include "server_arrowtarget.h"
|
||||
|
||||
#include <QMutex>
|
||||
|
||||
class Server_Game;
|
||||
class Server_AbstractUserInterface;
|
||||
class ServerInfo_User;
|
||||
class ServerInfo_Player;
|
||||
class ServerInfo_PlayerProperties;
|
||||
class GameEventContainer;
|
||||
class GameEventStorage;
|
||||
class ResponseContainer;
|
||||
class GameCommand;
|
||||
|
||||
class Command_KickFromGame;
|
||||
class Command_LeaveGame;
|
||||
class Command_GameSay;
|
||||
class Command_Shuffle;
|
||||
class Command_Mulligan;
|
||||
class Command_RollDie;
|
||||
class Command_DrawCards;
|
||||
class Command_UndoDraw;
|
||||
class Command_FlipCard;
|
||||
class Command_AttachCard;
|
||||
class Command_CreateToken;
|
||||
class Command_CreateArrow;
|
||||
class Command_DeleteArrow;
|
||||
class Command_SetCardAttr;
|
||||
class Command_SetCardCounter;
|
||||
class Command_IncCardCounter;
|
||||
class Command_ReadyStart;
|
||||
class Command_Concede;
|
||||
class Command_Unconcede;
|
||||
class Command_Judge;
|
||||
class Command_IncCounter;
|
||||
class Command_CreateCounter;
|
||||
class Command_SetCounter;
|
||||
class Command_DelCounter;
|
||||
class Command_NextTurn;
|
||||
class Command_SetActivePhase;
|
||||
class Command_DumpZone;
|
||||
class Command_RevealCards;
|
||||
class Command_ReverseTurn;
|
||||
class Command_MoveCard;
|
||||
class Command_SetSideboardPlan;
|
||||
class Command_DeckSelect;
|
||||
class Command_SetSideboardLock;
|
||||
class Command_ChangeZoneProperties;
|
||||
|
||||
class Server_AbstractParticipant : public Server_ArrowTarget, public ServerInfo_User_Container
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
Server_Game *game;
|
||||
Server_AbstractUserInterface *userInterface;
|
||||
int pingTime;
|
||||
int playerId;
|
||||
bool spectator;
|
||||
bool judge;
|
||||
virtual void getPlayerProperties(ServerInfo_PlayerProperties &result);
|
||||
mutable QMutex playerMutex;
|
||||
|
||||
public:
|
||||
Server_AbstractParticipant(Server_Game *_game,
|
||||
int _playerId,
|
||||
const ServerInfo_User &_userInfo,
|
||||
bool _judge,
|
||||
Server_AbstractUserInterface *_handler);
|
||||
~Server_AbstractParticipant() override;
|
||||
virtual void prepareDestroy()
|
||||
{
|
||||
removeFromGame();
|
||||
};
|
||||
void removeFromGame();
|
||||
Server_AbstractUserInterface *getUserInterface() const
|
||||
{
|
||||
return userInterface;
|
||||
}
|
||||
void setUserInterface(Server_AbstractUserInterface *_userInterface);
|
||||
void disconnectClient();
|
||||
|
||||
int getPlayerId() const
|
||||
{
|
||||
return playerId;
|
||||
}
|
||||
bool getSpectator() const
|
||||
{
|
||||
return spectator;
|
||||
}
|
||||
bool getJudge() const
|
||||
{
|
||||
return judge;
|
||||
}
|
||||
Server_Game *getGame() const
|
||||
{
|
||||
return game;
|
||||
}
|
||||
int getPingTime() const
|
||||
{
|
||||
return pingTime;
|
||||
}
|
||||
bool updatePingTime();
|
||||
void getProperties(ServerInfo_PlayerProperties &result, bool withUserInfo);
|
||||
|
||||
virtual Response::ResponseCode
|
||||
cmdLeaveGame(const Command_LeaveGame &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdKickFromGame(const Command_KickFromGame &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode cmdConcede(const Command_Concede &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdUnconcede(const Command_Unconcede &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode cmdJudge(const Command_Judge &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdSetSideboardLock(const Command_SetSideboardLock &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode cmdGameSay(const Command_GameSay &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode cmdShuffle(const Command_Shuffle &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdMulligan(const Command_Mulligan &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdRollDie(const Command_RollDie &cmd, ResponseContainer &rc, GameEventStorage &ges) const;
|
||||
virtual Response::ResponseCode
|
||||
cmdDrawCards(const Command_DrawCards &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdUndoDraw(const Command_UndoDraw &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdMoveCard(const Command_MoveCard &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdFlipCard(const Command_FlipCard &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdDeleteArrow(const Command_DeleteArrow &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdSetCardAttr(const Command_SetCardAttr &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdSetCardCounter(const Command_SetCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdIncCardCounter(const Command_IncCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdNextTurn(const Command_NextTurn &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdDumpZone(const Command_DumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdRevealCards(const Command_RevealCards &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
|
||||
Response::ResponseCode processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges);
|
||||
void sendGameEvent(const GameEventContainer &event);
|
||||
|
||||
virtual void
|
||||
getInfo(ServerInfo_Player *info, Server_AbstractParticipant *recipient, bool omniscient, bool withUserInfo);
|
||||
};
|
||||
|
||||
#endif
|
||||
28
common/server/game/server_arrow.cpp
Normal file
28
common/server/game/server_arrow.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include "server_arrow.h"
|
||||
|
||||
#include "pb/serverinfo_arrow.pb.h"
|
||||
#include "server_card.h"
|
||||
#include "server_cardzone.h"
|
||||
#include "server_player.h"
|
||||
|
||||
Server_Arrow::Server_Arrow(int _id, Server_Card *_startCard, Server_ArrowTarget *_targetItem, const color &_arrowColor)
|
||||
: id(_id), startCard(_startCard), targetItem(_targetItem), arrowColor(_arrowColor)
|
||||
{
|
||||
}
|
||||
|
||||
void Server_Arrow::getInfo(ServerInfo_Arrow *info)
|
||||
{
|
||||
info->set_id(id);
|
||||
info->set_start_player_id(startCard->getZone()->getPlayer()->getPlayerId());
|
||||
info->set_start_zone(startCard->getZone()->getName().toStdString());
|
||||
info->set_start_card_id(startCard->getId());
|
||||
info->mutable_arrow_color()->CopyFrom(arrowColor);
|
||||
|
||||
Server_Card *targetCard = qobject_cast<Server_Card *>(targetItem);
|
||||
if (targetCard) {
|
||||
info->set_target_player_id(targetCard->getZone()->getPlayer()->getPlayerId());
|
||||
info->set_target_zone(targetCard->getZone()->getName().toStdString());
|
||||
info->set_target_card_id(targetCard->getId());
|
||||
} else
|
||||
info->set_target_player_id(static_cast<Server_Player *>(targetItem)->getPlayerId());
|
||||
}
|
||||
52
common/server/game/server_arrow.h
Normal file
52
common/server/game/server_arrow.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef SERVER_ARROW_H
|
||||
#define SERVER_ARROW_H
|
||||
|
||||
#include "pb/color.pb.h"
|
||||
|
||||
class Server_Card;
|
||||
class Server_ArrowTarget;
|
||||
class ServerInfo_Arrow;
|
||||
|
||||
class Server_Arrow
|
||||
{
|
||||
private:
|
||||
int id;
|
||||
Server_Card *startCard;
|
||||
Server_ArrowTarget *targetItem;
|
||||
color arrowColor;
|
||||
|
||||
public:
|
||||
Server_Arrow(int _id, Server_Card *_startCard, Server_ArrowTarget *_targetItem, const color &_arrowColor);
|
||||
int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
void setId(int _id)
|
||||
{
|
||||
id = _id;
|
||||
}
|
||||
Server_Card *getStartCard() const
|
||||
{
|
||||
return startCard;
|
||||
}
|
||||
void setStartCard(Server_Card *startCard_)
|
||||
{
|
||||
startCard = startCard_;
|
||||
}
|
||||
Server_ArrowTarget *getTargetItem() const
|
||||
{
|
||||
return targetItem;
|
||||
}
|
||||
void setTargetItem(Server_ArrowTarget *targetItem_)
|
||||
{
|
||||
targetItem = targetItem_;
|
||||
}
|
||||
const color &getColor() const
|
||||
{
|
||||
return arrowColor;
|
||||
}
|
||||
|
||||
void getInfo(ServerInfo_Arrow *info);
|
||||
};
|
||||
|
||||
#endif
|
||||
2
common/server/game/server_arrowtarget.cpp
Normal file
2
common/server/game/server_arrowtarget.cpp
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "server_arrowtarget.h"
|
||||
11
common/server/game/server_arrowtarget.h
Normal file
11
common/server/game/server_arrowtarget.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef SERVER_ARROWTARGET_H
|
||||
#define SERVER_ARROWTARGET_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class Server_ArrowTarget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
174
common/server/game/server_card.cpp
Normal file
174
common/server/game/server_card.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Max-Wilhelm Bruker *
|
||||
* brukie@laptop *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#include "server_card.h"
|
||||
|
||||
#include "pb/event_set_card_attr.pb.h"
|
||||
#include "pb/event_set_card_counter.pb.h"
|
||||
#include "pb/serverinfo_card.pb.h"
|
||||
#include "server_cardzone.h"
|
||||
#include "server_player.h"
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
Server_Card::Server_Card(const CardRef &cardRef, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone)
|
||||
: zone(_zone), id(_id), coord_x(_coord_x), coord_y(_coord_y), cardRef(cardRef), tapped(false), attacking(false),
|
||||
facedown(false), destroyOnZoneChange(false), doesntUntap(false), parentCard(0), stashedCard(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Server_Card::~Server_Card()
|
||||
{
|
||||
// setParentCard(0) leads to the item being removed from our list, so we can't iterate properly
|
||||
while (!attachedCards.isEmpty())
|
||||
attachedCards.first()->setParentCard(0);
|
||||
|
||||
if (parentCard)
|
||||
parentCard->removeAttachedCard(this);
|
||||
|
||||
if (stashedCard) {
|
||||
stashedCard->deleteLater();
|
||||
stashedCard = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Server_Card::resetState(bool keepAnnotations)
|
||||
{
|
||||
counters.clear();
|
||||
setTapped(false);
|
||||
setAttacking(false);
|
||||
setPT(QString());
|
||||
if (!keepAnnotations) {
|
||||
setAnnotation(QString());
|
||||
}
|
||||
setDoesntUntap(false);
|
||||
}
|
||||
|
||||
QString Server_Card::setAttribute(CardAttribute attribute, const QString &avalue, bool allCards)
|
||||
{
|
||||
if (attribute == AttrTapped && avalue != "1" && allCards && doesntUntap)
|
||||
return QVariant(tapped).toString();
|
||||
|
||||
return setAttribute(attribute, avalue);
|
||||
}
|
||||
|
||||
QString Server_Card::setAttribute(CardAttribute attribute, const QString &avalue, Event_SetCardAttr *event)
|
||||
{
|
||||
if (event)
|
||||
event->set_attribute(attribute);
|
||||
|
||||
switch (attribute) {
|
||||
case AttrTapped: {
|
||||
setTapped(avalue == "1");
|
||||
break;
|
||||
}
|
||||
case AttrAttacking:
|
||||
setAttacking(avalue == "1");
|
||||
break;
|
||||
case AttrFaceDown:
|
||||
setFaceDown(avalue == "1");
|
||||
break;
|
||||
case AttrColor:
|
||||
setColor(avalue);
|
||||
break;
|
||||
case AttrPT:
|
||||
setPT(avalue);
|
||||
if (event)
|
||||
event->set_attr_value(getPT().toStdString());
|
||||
return getPT();
|
||||
case AttrAnnotation:
|
||||
setAnnotation(avalue);
|
||||
break;
|
||||
case AttrDoesntUntap:
|
||||
setDoesntUntap(avalue == "1");
|
||||
break;
|
||||
}
|
||||
if (event)
|
||||
event->set_attr_value(avalue.toStdString());
|
||||
return avalue;
|
||||
}
|
||||
|
||||
void Server_Card::setCounter(int _id, int value, Event_SetCardCounter *event)
|
||||
{
|
||||
if (value)
|
||||
counters.insert(_id, value);
|
||||
else
|
||||
counters.remove(_id);
|
||||
|
||||
if (event) {
|
||||
event->set_counter_id(_id);
|
||||
event->set_counter_value(value);
|
||||
}
|
||||
}
|
||||
|
||||
void Server_Card::setParentCard(Server_Card *_parentCard)
|
||||
{
|
||||
if (parentCard)
|
||||
parentCard->removeAttachedCard(this);
|
||||
parentCard = _parentCard;
|
||||
if (parentCard)
|
||||
parentCard->addAttachedCard(this);
|
||||
}
|
||||
|
||||
void Server_Card::getInfo(ServerInfo_Card *info)
|
||||
{
|
||||
QString displayedName = facedown ? QString() : cardRef.name;
|
||||
|
||||
info->set_id(id);
|
||||
info->set_provider_id(cardRef.providerId.toStdString());
|
||||
info->set_name(displayedName.toStdString());
|
||||
info->set_x(coord_x);
|
||||
info->set_y(coord_y);
|
||||
if (facedown) {
|
||||
info->set_face_down(true);
|
||||
}
|
||||
info->set_tapped(tapped);
|
||||
if (attacking) {
|
||||
info->set_attacking(true);
|
||||
}
|
||||
if (!color.isEmpty()) {
|
||||
info->set_color(color.toStdString());
|
||||
}
|
||||
if (!ptString.isEmpty()) {
|
||||
info->set_pt(ptString.toStdString());
|
||||
}
|
||||
if (!annotation.isEmpty()) {
|
||||
info->set_annotation(annotation.toStdString());
|
||||
}
|
||||
if (destroyOnZoneChange) {
|
||||
info->set_destroy_on_zone_change(true);
|
||||
}
|
||||
if (doesntUntap) {
|
||||
info->set_doesnt_untap(true);
|
||||
}
|
||||
|
||||
QMapIterator<int, int> cardCounterIterator(counters);
|
||||
while (cardCounterIterator.hasNext()) {
|
||||
cardCounterIterator.next();
|
||||
ServerInfo_CardCounter *counterInfo = info->add_counter_list();
|
||||
counterInfo->set_id(cardCounterIterator.key());
|
||||
counterInfo->set_value(cardCounterIterator.value());
|
||||
}
|
||||
|
||||
if (parentCard) {
|
||||
info->set_attach_player_id(parentCard->getZone()->getPlayer()->getPlayerId());
|
||||
info->set_attach_zone(parentCard->getZone()->getName().toStdString());
|
||||
info->set_attach_card_id(parentCard->getId());
|
||||
}
|
||||
}
|
||||
227
common/server/game/server_card.h
Normal file
227
common/server/game/server_card.h
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Max-Wilhelm Bruker *
|
||||
* brukie@laptop *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#ifndef SERVER_CARD_H
|
||||
#define SERVER_CARD_H
|
||||
|
||||
#include "../../card_ref.h"
|
||||
#include "pb/card_attributes.pb.h"
|
||||
#include "pb/serverinfo_card.pb.h"
|
||||
#include "server_arrowtarget.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
class Server_CardZone;
|
||||
class Event_SetCardCounter;
|
||||
class Event_SetCardAttr;
|
||||
|
||||
class Server_Card : public Server_ArrowTarget
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
Server_CardZone *zone;
|
||||
int id;
|
||||
int coord_x, coord_y;
|
||||
CardRef cardRef;
|
||||
QMap<int, int> counters;
|
||||
bool tapped;
|
||||
bool attacking;
|
||||
bool facedown;
|
||||
QString color;
|
||||
QString ptString;
|
||||
QString annotation;
|
||||
bool destroyOnZoneChange;
|
||||
bool doesntUntap;
|
||||
|
||||
Server_Card *parentCard;
|
||||
QList<Server_Card *> attachedCards;
|
||||
Server_Card *stashedCard;
|
||||
|
||||
public:
|
||||
Server_Card(const CardRef &cardRef, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone = nullptr);
|
||||
~Server_Card() override;
|
||||
|
||||
Server_CardZone *getZone() const
|
||||
{
|
||||
return zone;
|
||||
}
|
||||
void setZone(Server_CardZone *_zone)
|
||||
{
|
||||
zone = _zone;
|
||||
}
|
||||
|
||||
int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
CardRef getCardRef() const
|
||||
{
|
||||
return cardRef;
|
||||
}
|
||||
QString getProviderId() const
|
||||
{
|
||||
return cardRef.providerId;
|
||||
}
|
||||
int getX() const
|
||||
{
|
||||
return coord_x;
|
||||
}
|
||||
int getY() const
|
||||
{
|
||||
return coord_y;
|
||||
}
|
||||
QString getName() const
|
||||
{
|
||||
return cardRef.name;
|
||||
}
|
||||
const QMap<int, int> &getCounters() const
|
||||
{
|
||||
return counters;
|
||||
}
|
||||
int getCounter(int counter_id) const
|
||||
{
|
||||
return counters.value(counter_id, 0);
|
||||
}
|
||||
bool getTapped() const
|
||||
{
|
||||
return tapped;
|
||||
}
|
||||
bool getAttacking() const
|
||||
{
|
||||
return attacking;
|
||||
}
|
||||
bool getFaceDown() const
|
||||
{
|
||||
return facedown;
|
||||
}
|
||||
QString getColor() const
|
||||
{
|
||||
return color;
|
||||
}
|
||||
QString getPT() const
|
||||
{
|
||||
return ptString;
|
||||
}
|
||||
QString getAnnotation() const
|
||||
{
|
||||
return annotation;
|
||||
}
|
||||
bool getDoesntUntap() const
|
||||
{
|
||||
return doesntUntap;
|
||||
}
|
||||
bool getDestroyOnZoneChange() const
|
||||
{
|
||||
return destroyOnZoneChange;
|
||||
}
|
||||
Server_Card *getParentCard() const
|
||||
{
|
||||
return parentCard;
|
||||
}
|
||||
const QList<Server_Card *> &getAttachedCards() const
|
||||
{
|
||||
return attachedCards;
|
||||
}
|
||||
|
||||
void setId(int _id)
|
||||
{
|
||||
id = _id;
|
||||
}
|
||||
void setCoords(int x, int y)
|
||||
{
|
||||
coord_x = x;
|
||||
coord_y = y;
|
||||
}
|
||||
void setCardRef(const CardRef &_cardRef)
|
||||
{
|
||||
cardRef = _cardRef;
|
||||
}
|
||||
void setCounter(int _id, int value, Event_SetCardCounter *event = nullptr);
|
||||
void setTapped(bool _tapped)
|
||||
{
|
||||
tapped = _tapped;
|
||||
}
|
||||
void setAttacking(bool _attacking)
|
||||
{
|
||||
attacking = _attacking;
|
||||
}
|
||||
void setFaceDown(bool _facedown)
|
||||
{
|
||||
facedown = _facedown;
|
||||
}
|
||||
void setColor(const QString &_color)
|
||||
{
|
||||
color = _color;
|
||||
}
|
||||
void setPT(const QString &_pt)
|
||||
{
|
||||
ptString = _pt;
|
||||
}
|
||||
void setAnnotation(const QString &_annotation)
|
||||
{
|
||||
annotation = _annotation;
|
||||
}
|
||||
void setDestroyOnZoneChange(bool _destroy)
|
||||
{
|
||||
destroyOnZoneChange = _destroy;
|
||||
}
|
||||
void setDoesntUntap(bool _doesntUntap)
|
||||
{
|
||||
doesntUntap = _doesntUntap;
|
||||
}
|
||||
void setParentCard(Server_Card *_parentCard);
|
||||
void addAttachedCard(Server_Card *card)
|
||||
{
|
||||
attachedCards.append(card);
|
||||
}
|
||||
void removeAttachedCard(Server_Card *card)
|
||||
{
|
||||
attachedCards.removeOne(card);
|
||||
}
|
||||
void setStashedCard(Server_Card *card)
|
||||
{
|
||||
// setStashedCard should only be called on creation of a new card, so
|
||||
// there should never be an already existing stashed card.
|
||||
Q_ASSERT(!stashedCard);
|
||||
|
||||
// Stashed cards can't themselves have stashed cards, and tokens can't
|
||||
// be stashed.
|
||||
if (card->stashedCard || card->getDestroyOnZoneChange()) {
|
||||
stashedCard = card->takeStashedCard();
|
||||
card->deleteLater();
|
||||
} else {
|
||||
stashedCard = card;
|
||||
}
|
||||
}
|
||||
Server_Card *takeStashedCard()
|
||||
{
|
||||
Server_Card *oldStashedCard = stashedCard;
|
||||
stashedCard = nullptr;
|
||||
return oldStashedCard;
|
||||
}
|
||||
|
||||
void resetState(bool keepAnnotations = false);
|
||||
QString setAttribute(CardAttribute attribute, const QString &avalue, bool allCards);
|
||||
QString setAttribute(CardAttribute attribute, const QString &avalue, Event_SetCardAttr *event = nullptr);
|
||||
|
||||
void getInfo(ServerInfo_Card *info);
|
||||
};
|
||||
|
||||
#endif
|
||||
339
common/server/game/server_cardzone.cpp
Normal file
339
common/server/game/server_cardzone.cpp
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Max-Wilhelm Bruker *
|
||||
* brukie@laptop *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#include "server_cardzone.h"
|
||||
|
||||
#include "../rng_abstract.h"
|
||||
#include "pb/command_move_card.pb.h"
|
||||
#include "server_card.h"
|
||||
#include "server_player.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSet>
|
||||
|
||||
Server_CardZone::Server_CardZone(Server_Player *_player,
|
||||
const QString &_name,
|
||||
bool _has_coords,
|
||||
ServerInfo_Zone::ZoneType _type)
|
||||
: player(_player), name(_name), has_coords(_has_coords), type(_type), cardsBeingLookedAt(0),
|
||||
alwaysRevealTopCard(false), alwaysLookAtTopCard(false)
|
||||
{
|
||||
}
|
||||
|
||||
Server_CardZone::~Server_CardZone()
|
||||
{
|
||||
qDebug() << "Server_CardZone destructor:" << name;
|
||||
clear();
|
||||
}
|
||||
|
||||
void Server_CardZone::shuffle(int start, int end)
|
||||
{
|
||||
cardsBeingLookedAt = 0;
|
||||
|
||||
// Size 0 or 1 decks are sorted
|
||||
if (cards.size() < 2)
|
||||
return;
|
||||
|
||||
// Negative numbers signify positions starting at the end of the
|
||||
// zone convert these to actual indexes.
|
||||
if (end < 0)
|
||||
end += cards.size();
|
||||
|
||||
if (start < 0)
|
||||
start += cards.size();
|
||||
|
||||
if (start < 0 || end < 0 || start >= cards.size() || end >= cards.size())
|
||||
return;
|
||||
|
||||
for (int i = end; i > start; i--) {
|
||||
int j = rng->rand(start, i);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
|
||||
cards.swapItemsAt(j, i);
|
||||
#else
|
||||
cards.swap(j, i);
|
||||
#endif
|
||||
}
|
||||
playersWithWritePermission.clear();
|
||||
}
|
||||
|
||||
void Server_CardZone::removeCardFromCoordMap(Server_Card *card, int oldX, int oldY)
|
||||
{
|
||||
if (oldX < 0)
|
||||
return;
|
||||
|
||||
const int baseX = (oldX / 3) * 3;
|
||||
QMap<int, Server_Card *> &coordMap = coordinateMap[oldY];
|
||||
|
||||
if (coordMap.contains(baseX) && coordMap.contains(baseX + 1) && coordMap.contains(baseX + 2))
|
||||
// If the removal of this card has opened up a previously full pile...
|
||||
freePilesMap[oldY].insert(coordMap.value(baseX)->getName(), baseX);
|
||||
|
||||
coordMap.remove(oldX);
|
||||
|
||||
if (!(coordMap.contains(baseX) && coordMap.value(baseX)->getName() == card->getName()) &&
|
||||
!(coordMap.contains(baseX + 1) && coordMap.value(baseX + 1)->getName() == card->getName()) &&
|
||||
!(coordMap.contains(baseX + 2) && coordMap.value(baseX + 2)->getName() == card->getName()))
|
||||
// If this card was the last one with this name...
|
||||
freePilesMap[oldY].remove(card->getName(), baseX);
|
||||
|
||||
if (!coordMap.contains(baseX) && !coordMap.contains(baseX + 1) && !coordMap.contains(baseX + 2)) {
|
||||
// If the removal of this card has freed a whole pile, i.e. it was the last card in it...
|
||||
if (baseX < freeSpaceMap[oldY])
|
||||
freeSpaceMap[oldY] = baseX;
|
||||
}
|
||||
}
|
||||
|
||||
void Server_CardZone::insertCardIntoCoordMap(Server_Card *card, int x, int y)
|
||||
{
|
||||
if (x < 0)
|
||||
return;
|
||||
|
||||
coordinateMap[y].insert(x, card);
|
||||
if (!(x % 3)) {
|
||||
if (!card->getFaceDown() && !freePilesMap[y].contains(card->getName(), x) && card->getAttachedCards().isEmpty())
|
||||
freePilesMap[y].insert(card->getName(), x);
|
||||
if (freeSpaceMap[y] == x) {
|
||||
int nextFreeX = x;
|
||||
do {
|
||||
nextFreeX += 3;
|
||||
} while (coordinateMap[y].contains(nextFreeX) || coordinateMap[y].contains(nextFreeX + 1) ||
|
||||
coordinateMap[y].contains(nextFreeX + 2));
|
||||
freeSpaceMap[y] = nextFreeX;
|
||||
}
|
||||
} else if (!((x - 2) % 3)) {
|
||||
const int baseX = (x / 3) * 3;
|
||||
freePilesMap[y].remove(coordinateMap[y].value(baseX)->getName(), baseX);
|
||||
}
|
||||
}
|
||||
|
||||
int Server_CardZone::removeCard(Server_Card *card)
|
||||
{
|
||||
bool wasLookedAt;
|
||||
return removeCard(card, wasLookedAt);
|
||||
}
|
||||
|
||||
int Server_CardZone::removeCard(Server_Card *card, bool &wasLookedAt)
|
||||
{
|
||||
int index = cards.indexOf(card);
|
||||
wasLookedAt = isCardAtPosLookedAt(index);
|
||||
if (wasLookedAt && cardsBeingLookedAt > 0) {
|
||||
cardsBeingLookedAt -= 1;
|
||||
}
|
||||
cards.removeAt(index);
|
||||
if (has_coords) {
|
||||
removeCardFromCoordMap(card, card->getX(), card->getY());
|
||||
}
|
||||
card->setZone(nullptr);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
Server_Card *Server_CardZone::getCard(int id, int *position, bool remove)
|
||||
{
|
||||
if (type != ServerInfo_Zone::HiddenZone) {
|
||||
for (int i = 0; i < cards.size(); ++i) {
|
||||
Server_Card *tmp = cards[i];
|
||||
if (tmp->getId() == id) {
|
||||
if (position)
|
||||
*position = i;
|
||||
if (remove) {
|
||||
cards.removeAt(i);
|
||||
tmp->setZone(nullptr);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
} else {
|
||||
if ((id >= cards.size()) || (id < 0))
|
||||
return nullptr;
|
||||
Server_Card *tmp = cards[id];
|
||||
if (position)
|
||||
*position = id;
|
||||
if (remove) {
|
||||
cards.removeAt(id);
|
||||
tmp->setZone(nullptr);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
bool Server_CardZone::isCardAtPosLookedAt(int pos) const
|
||||
{
|
||||
return type == ServerInfo_Zone::HiddenZone && (cardsBeingLookedAt == -1 || cardsBeingLookedAt > pos);
|
||||
}
|
||||
|
||||
int Server_CardZone::getFreeGridColumn(int x, int y, const QString &cardName, bool dontStackSameName) const
|
||||
{
|
||||
const QMap<int, Server_Card *> &coordMap = coordinateMap.value(y);
|
||||
if (x == -1) {
|
||||
if (!dontStackSameName && freePilesMap[y].contains(cardName)) {
|
||||
x = (freePilesMap[y].value(cardName) / 3) * 3;
|
||||
|
||||
if (coordMap.contains(x) && (coordMap[x]->getFaceDown() || !coordMap[x]->getAttachedCards().isEmpty())) {
|
||||
// don't pile up on: 1. facedown cards 2. cards with attached cards
|
||||
} else if (!coordMap.contains(x))
|
||||
return x;
|
||||
else if (!coordMap.contains(x + 1))
|
||||
return x + 1;
|
||||
else
|
||||
return x + 2;
|
||||
}
|
||||
} else if (x >= 0) {
|
||||
int resultX = 0;
|
||||
x = (x / 3) * 3;
|
||||
if (!coordMap.contains(x))
|
||||
resultX = x;
|
||||
else if (!coordMap.value(x)->getAttachedCards().isEmpty()) {
|
||||
resultX = x;
|
||||
x = -1;
|
||||
} else if (!coordMap.contains(x + 1))
|
||||
resultX = x + 1;
|
||||
else if (!coordMap.contains(x + 2))
|
||||
resultX = x + 2;
|
||||
else {
|
||||
resultX = x;
|
||||
x = -1;
|
||||
}
|
||||
if (x < 0)
|
||||
while (coordMap.contains(resultX))
|
||||
resultX += 3;
|
||||
|
||||
return resultX;
|
||||
}
|
||||
|
||||
return freeSpaceMap[y];
|
||||
}
|
||||
|
||||
bool Server_CardZone::isColumnStacked(int x, int y) const
|
||||
{
|
||||
if (!has_coords)
|
||||
return false;
|
||||
|
||||
return coordinateMap[y].contains((x / 3) * 3 + 1);
|
||||
}
|
||||
|
||||
bool Server_CardZone::isColumnEmpty(int x, int y) const
|
||||
{
|
||||
if (!has_coords)
|
||||
return true;
|
||||
|
||||
return !coordinateMap[y].contains((x / 3) * 3);
|
||||
}
|
||||
|
||||
void Server_CardZone::moveCardInRow(GameEventStorage &ges, Server_Card *card, int x, int y)
|
||||
{
|
||||
auto *cardToMove = new CardToMove;
|
||||
cardToMove->set_card_id(card->getId());
|
||||
player->moveCard(ges, this, QList<const CardToMove *>() << cardToMove, this, x, y, false, false);
|
||||
delete cardToMove;
|
||||
}
|
||||
|
||||
void Server_CardZone::fixFreeSpaces(GameEventStorage &ges)
|
||||
{
|
||||
if (!has_coords)
|
||||
return;
|
||||
|
||||
QSet<QPair<int, int>> placesToLook;
|
||||
for (auto &card : cards)
|
||||
placesToLook.insert(QPair<int, int>((card->getX() / 3) * 3, card->getY()));
|
||||
|
||||
QSetIterator<QPair<int, int>> placeIterator(placesToLook);
|
||||
while (placeIterator.hasNext()) {
|
||||
const QPair<int, int> &foo = placeIterator.next();
|
||||
int baseX = foo.first;
|
||||
int y = foo.second;
|
||||
|
||||
if (!coordinateMap[y].contains(baseX)) {
|
||||
if (coordinateMap[y].contains(baseX + 1))
|
||||
moveCardInRow(ges, coordinateMap[y].value(baseX + 1), baseX, y);
|
||||
else if (coordinateMap[y].contains(baseX + 2)) {
|
||||
moveCardInRow(ges, coordinateMap[y].value(baseX + 2), baseX, y);
|
||||
continue;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
if (!coordinateMap[y].contains(baseX + 1) && coordinateMap[y].contains(baseX + 2))
|
||||
moveCardInRow(ges, coordinateMap[y].value(baseX + 2), baseX + 1, y);
|
||||
}
|
||||
}
|
||||
|
||||
void Server_CardZone::updateCardCoordinates(Server_Card *card, int oldX, int oldY)
|
||||
{
|
||||
if (!has_coords)
|
||||
return;
|
||||
|
||||
if (oldX != -1)
|
||||
removeCardFromCoordMap(card, oldX, oldY);
|
||||
insertCardIntoCoordMap(card, card->getX(), card->getY());
|
||||
}
|
||||
|
||||
void Server_CardZone::insertCard(Server_Card *card, int x, int y)
|
||||
{
|
||||
if (hasCoords()) {
|
||||
card->setCoords(x, y);
|
||||
cards.append(card);
|
||||
insertCardIntoCoordMap(card, x, y);
|
||||
} else {
|
||||
card->setCoords(0, 0);
|
||||
if (0 <= x && x < cards.length()) {
|
||||
cards.insert(x, card);
|
||||
} else {
|
||||
cards.append(card);
|
||||
}
|
||||
}
|
||||
card->setZone(this);
|
||||
}
|
||||
|
||||
void Server_CardZone::clear()
|
||||
{
|
||||
for (auto card : cards)
|
||||
delete card;
|
||||
cards.clear();
|
||||
coordinateMap.clear();
|
||||
freePilesMap.clear();
|
||||
freeSpaceMap.clear();
|
||||
playersWithWritePermission.clear();
|
||||
cardsBeingLookedAt = 0;
|
||||
}
|
||||
|
||||
void Server_CardZone::addWritePermission(int playerId)
|
||||
{
|
||||
playersWithWritePermission.insert(playerId);
|
||||
}
|
||||
|
||||
void Server_CardZone::getInfo(ServerInfo_Zone *info, Server_AbstractParticipant *recipient, bool omniscient)
|
||||
{
|
||||
info->set_name(name.toStdString());
|
||||
info->set_type(type);
|
||||
info->set_with_coords(has_coords);
|
||||
info->set_card_count(static_cast<int>(cards.size()));
|
||||
info->set_always_reveal_top_card(alwaysRevealTopCard);
|
||||
info->set_always_look_at_top_card(alwaysLookAtTopCard);
|
||||
|
||||
const bool selfPlayerAsking = recipient == player || omniscient;
|
||||
const bool zonesSelfCanSee = type != ServerInfo_Zone::HiddenZone;
|
||||
const bool otherPlayerAsking = recipient != player;
|
||||
const bool zonesOthersCanSee = type == ServerInfo_Zone::PublicZone;
|
||||
if ((selfPlayerAsking && zonesSelfCanSee) || (otherPlayerAsking && zonesOthersCanSee)) {
|
||||
QListIterator<Server_Card *> cardIterator(cards);
|
||||
while (cardIterator.hasNext())
|
||||
cardIterator.next()->getInfo(info->add_card_list());
|
||||
}
|
||||
}
|
||||
125
common/server/game/server_cardzone.h
Normal file
125
common/server/game/server_cardzone.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Max-Wilhelm Bruker *
|
||||
* brukie@laptop *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#ifndef SERVER_CARDZONE_H
|
||||
#define SERVER_CARDZONE_H
|
||||
|
||||
#include "pb/serverinfo_zone.pb.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
|
||||
class Server_Card;
|
||||
class Server_Player;
|
||||
class Server_AbstractParticipant;
|
||||
class Server_Game;
|
||||
class GameEventStorage;
|
||||
|
||||
class Server_CardZone
|
||||
{
|
||||
private:
|
||||
Server_Player *player;
|
||||
QString name;
|
||||
bool has_coords; // having coords means this zone has x and y coordinates
|
||||
ServerInfo_Zone::ZoneType type;
|
||||
int cardsBeingLookedAt;
|
||||
QSet<int> playersWithWritePermission;
|
||||
bool alwaysRevealTopCard;
|
||||
bool alwaysLookAtTopCard;
|
||||
QList<Server_Card *> cards;
|
||||
QMap<int, QMap<int, Server_Card *>> coordinateMap; // y -> (x -> card)
|
||||
QMap<int, QMultiMap<QString, int>> freePilesMap; // y -> (cardName -> x)
|
||||
QMap<int, int> freeSpaceMap; // y -> x
|
||||
void removeCardFromCoordMap(Server_Card *card, int oldX, int oldY);
|
||||
void insertCardIntoCoordMap(Server_Card *card, int x, int y);
|
||||
|
||||
public:
|
||||
Server_CardZone(Server_Player *_player, const QString &_name, bool _has_coords, ServerInfo_Zone::ZoneType _type);
|
||||
~Server_CardZone();
|
||||
|
||||
const QList<Server_Card *> &getCards() const
|
||||
{
|
||||
return cards;
|
||||
}
|
||||
int removeCard(Server_Card *card);
|
||||
int removeCard(Server_Card *card, bool &wasLookedAt);
|
||||
Server_Card *getCard(int id, int *position = nullptr, bool remove = false);
|
||||
|
||||
int getCardsBeingLookedAt() const
|
||||
{
|
||||
return cardsBeingLookedAt;
|
||||
}
|
||||
void setCardsBeingLookedAt(int _cardsBeingLookedAt)
|
||||
{
|
||||
cardsBeingLookedAt = qMax(0, _cardsBeingLookedAt);
|
||||
}
|
||||
bool isCardAtPosLookedAt(int pos) const;
|
||||
bool hasCoords() const
|
||||
{
|
||||
return has_coords;
|
||||
}
|
||||
ServerInfo_Zone::ZoneType getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
QString getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
Server_Player *getPlayer() const
|
||||
{
|
||||
return player;
|
||||
}
|
||||
void getInfo(ServerInfo_Zone *info, Server_AbstractParticipant *recipient, bool omniscient);
|
||||
|
||||
int getFreeGridColumn(int x, int y, const QString &cardName, bool dontStackSameName) const;
|
||||
bool isColumnEmpty(int x, int y) const;
|
||||
bool isColumnStacked(int x, int y) const;
|
||||
void fixFreeSpaces(GameEventStorage &ges);
|
||||
void moveCardInRow(GameEventStorage &ges, Server_Card *card, int x, int y);
|
||||
void insertCard(Server_Card *card, int x, int y);
|
||||
void updateCardCoordinates(Server_Card *card, int oldX, int oldY);
|
||||
void shuffle(int start = 0, int end = -1);
|
||||
void clear();
|
||||
void addWritePermission(int playerId);
|
||||
const QSet<int> &getPlayersWithWritePermission() const
|
||||
{
|
||||
return playersWithWritePermission;
|
||||
}
|
||||
bool getAlwaysRevealTopCard() const
|
||||
{
|
||||
return alwaysRevealTopCard;
|
||||
}
|
||||
void setAlwaysRevealTopCard(bool _alwaysRevealTopCard)
|
||||
{
|
||||
alwaysRevealTopCard = _alwaysRevealTopCard;
|
||||
}
|
||||
bool getAlwaysLookAtTopCard() const
|
||||
{
|
||||
return alwaysLookAtTopCard;
|
||||
}
|
||||
void setAlwaysLookAtTopCard(bool _alwaysLookAtTopCard)
|
||||
{
|
||||
alwaysLookAtTopCard = _alwaysLookAtTopCard;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
17
common/server/game/server_counter.cpp
Normal file
17
common/server/game/server_counter.cpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#include "server_counter.h"
|
||||
|
||||
#include "pb/serverinfo_counter.pb.h"
|
||||
|
||||
Server_Counter::Server_Counter(int _id, const QString &_name, const color &_counterColor, int _radius, int _count)
|
||||
: id(_id), name(_name), counterColor(_counterColor), radius(_radius), count(_count)
|
||||
{
|
||||
}
|
||||
|
||||
void Server_Counter::getInfo(ServerInfo_Counter *info)
|
||||
{
|
||||
info->set_id(id);
|
||||
info->set_name(name.toStdString());
|
||||
info->mutable_counter_color()->CopyFrom(counterColor);
|
||||
info->set_radius(radius);
|
||||
info->set_count(count);
|
||||
}
|
||||
71
common/server/game/server_counter.h
Normal file
71
common/server/game/server_counter.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Max-Wilhelm Bruker *
|
||||
* brukie@laptop *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#ifndef SERVER_COUNTER_H
|
||||
#define SERVER_COUNTER_H
|
||||
|
||||
#include "pb/color.pb.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
class ServerInfo_Counter;
|
||||
|
||||
class Server_Counter
|
||||
{
|
||||
protected:
|
||||
int id;
|
||||
QString name;
|
||||
color counterColor;
|
||||
int radius;
|
||||
int count;
|
||||
|
||||
public:
|
||||
Server_Counter(int _id, const QString &_name, const color &_counterColor, int _radius, int _count = 0);
|
||||
~Server_Counter()
|
||||
{
|
||||
}
|
||||
int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
QString getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
const color &getColor() const
|
||||
{
|
||||
return counterColor;
|
||||
}
|
||||
int getRadius() const
|
||||
{
|
||||
return radius;
|
||||
}
|
||||
int getCount() const
|
||||
{
|
||||
return count;
|
||||
}
|
||||
void setCount(int _count)
|
||||
{
|
||||
count = _count;
|
||||
}
|
||||
|
||||
void getInfo(ServerInfo_Counter *info);
|
||||
};
|
||||
|
||||
#endif
|
||||
825
common/server/game/server_game.cpp
Normal file
825
common/server/game/server_game.cpp
Normal file
|
|
@ -0,0 +1,825 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Max-Wilhelm Bruker *
|
||||
* brukie@laptop *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#include "server_game.h"
|
||||
|
||||
#include "../../deck_list.h"
|
||||
#include "../server.h"
|
||||
#include "../server_database_interface.h"
|
||||
#include "../server_protocolhandler.h"
|
||||
#include "../server_room.h"
|
||||
#include "pb/context_connection_state_changed.pb.h"
|
||||
#include "pb/context_deck_select.pb.h"
|
||||
#include "pb/context_ping_changed.pb.h"
|
||||
#include "pb/event_delete_arrow.pb.h"
|
||||
#include "pb/event_game_closed.pb.h"
|
||||
#include "pb/event_game_host_changed.pb.h"
|
||||
#include "pb/event_game_joined.pb.h"
|
||||
#include "pb/event_game_state_changed.pb.h"
|
||||
#include "pb/event_join.pb.h"
|
||||
#include "pb/event_kicked.pb.h"
|
||||
#include "pb/event_leave.pb.h"
|
||||
#include "pb/event_player_properties_changed.pb.h"
|
||||
#include "pb/event_replay_added.pb.h"
|
||||
#include "pb/event_set_active_phase.pb.h"
|
||||
#include "pb/event_set_active_player.pb.h"
|
||||
#include "pb/game_replay.pb.h"
|
||||
#include "pb/serverinfo_playerping.pb.h"
|
||||
#include "server_arrow.h"
|
||||
#include "server_card.h"
|
||||
#include "server_cardzone.h"
|
||||
#include "server_player.h"
|
||||
#include "server_spectator.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
Server_Game::Server_Game(const ServerInfo_User &_creatorInfo,
|
||||
int _gameId,
|
||||
const QString &_description,
|
||||
const QString &_password,
|
||||
int _maxPlayers,
|
||||
const QList<int> &_gameTypes,
|
||||
bool _onlyBuddies,
|
||||
bool _onlyRegistered,
|
||||
bool _spectatorsAllowed,
|
||||
bool _spectatorsNeedPassword,
|
||||
bool _spectatorsCanTalk,
|
||||
bool _spectatorsSeeEverything,
|
||||
int _startingLifeTotal,
|
||||
bool _shareDecklistsOnLoad,
|
||||
Server_Room *_room)
|
||||
: QObject(), room(_room), nextPlayerId(0), hostId(0), creatorInfo(new ServerInfo_User(_creatorInfo)),
|
||||
gameStarted(false), gameClosed(false), gameId(_gameId), password(_password), maxPlayers(_maxPlayers),
|
||||
gameTypes(_gameTypes), activePlayer(-1), activePhase(-1), onlyBuddies(_onlyBuddies),
|
||||
onlyRegistered(_onlyRegistered), spectatorsAllowed(_spectatorsAllowed),
|
||||
spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk),
|
||||
spectatorsSeeEverything(_spectatorsSeeEverything), startingLifeTotal(_startingLifeTotal),
|
||||
shareDecklistsOnLoad(_shareDecklistsOnLoad), inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0),
|
||||
firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()), pingClock(nullptr),
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
gameMutex()
|
||||
#else
|
||||
gameMutex(QMutex::Recursive)
|
||||
#endif
|
||||
{
|
||||
currentReplay = new GameReplay;
|
||||
currentReplay->set_replay_id(room->getServer()->getDatabaseInterface()->getNextReplayId());
|
||||
description = _description.simplified();
|
||||
|
||||
connect(this, &Server_Game::sigStartGameIfReady, this, &Server_Game::doStartGameIfReady, Qt::QueuedConnection);
|
||||
|
||||
getInfo(*currentReplay->mutable_game_info());
|
||||
|
||||
if (room->getServer()->getGameShouldPing()) {
|
||||
pingClock = new QTimer(this);
|
||||
connect(pingClock, &QTimer::timeout, this, &Server_Game::pingClockTimeout);
|
||||
pingClock->start(1000);
|
||||
}
|
||||
}
|
||||
|
||||
Server_Game::~Server_Game()
|
||||
{
|
||||
room->gamesLock.lockForWrite();
|
||||
gameMutex.lock();
|
||||
|
||||
gameClosed = true;
|
||||
sendGameEventContainer(prepareGameEvent(Event_GameClosed(), -1));
|
||||
for (auto *participant : participants.values()) {
|
||||
participant->prepareDestroy();
|
||||
}
|
||||
participants.clear();
|
||||
|
||||
room->removeGame(this);
|
||||
delete creatorInfo;
|
||||
creatorInfo = 0;
|
||||
|
||||
gameMutex.unlock();
|
||||
room->gamesLock.unlock();
|
||||
currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame);
|
||||
replayList.append(currentReplay);
|
||||
storeGameInformation();
|
||||
|
||||
for (auto *replay : replayList) {
|
||||
delete replay;
|
||||
}
|
||||
replayList.clear();
|
||||
|
||||
room = nullptr;
|
||||
currentReplay = nullptr;
|
||||
creatorInfo = nullptr;
|
||||
|
||||
if (pingClock) {
|
||||
delete pingClock;
|
||||
pingClock = nullptr;
|
||||
}
|
||||
|
||||
qDebug() << "Server_Game destructor: gameId=" << gameId;
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void Server_Game::storeGameInformation()
|
||||
{
|
||||
const ServerInfo_Game &gameInfo = replayList.first()->game_info();
|
||||
|
||||
Event_ReplayAdded replayEvent;
|
||||
ServerInfo_ReplayMatch *replayMatchInfo = replayEvent.mutable_match_info();
|
||||
replayMatchInfo->set_game_id(gameInfo.game_id());
|
||||
replayMatchInfo->set_room_name(room->getName().toStdString());
|
||||
replayMatchInfo->set_time_started(QDateTime::currentDateTime().addSecs(-secondsElapsed).toSecsSinceEpoch());
|
||||
replayMatchInfo->set_length(secondsElapsed);
|
||||
replayMatchInfo->set_game_name(gameInfo.description());
|
||||
|
||||
const QStringList &allGameTypes = room->getGameTypes();
|
||||
QStringList _gameTypes;
|
||||
for (int i = gameInfo.game_types_size() - 1; i >= 0; --i)
|
||||
_gameTypes.append(allGameTypes[gameInfo.game_types(i)]);
|
||||
|
||||
for (const auto &playerName : allPlayersEver) {
|
||||
replayMatchInfo->add_player_names(playerName.toStdString());
|
||||
}
|
||||
|
||||
for (int i = 0; i < replayList.size(); ++i) {
|
||||
ServerInfo_Replay *replayInfo = replayMatchInfo->add_replay_list();
|
||||
replayInfo->set_replay_id(replayList[i]->replay_id());
|
||||
replayInfo->set_replay_name(gameInfo.description());
|
||||
replayInfo->set_duration(replayList[i]->duration_seconds());
|
||||
}
|
||||
|
||||
SessionEvent *sessionEvent = Server_ProtocolHandler::prepareSessionEvent(replayEvent);
|
||||
Server *server = room->getServer();
|
||||
server->clientsLock.lockForRead();
|
||||
for (auto userName : allPlayersEver + allSpectatorsEver) {
|
||||
Server_AbstractUserInterface *userHandler = server->findUser(userName);
|
||||
if (userHandler && server->getStoreReplaysEnabled())
|
||||
userHandler->sendProtocolItem(*sessionEvent);
|
||||
}
|
||||
server->clientsLock.unlock();
|
||||
delete sessionEvent;
|
||||
|
||||
if (server->getStoreReplaysEnabled()) {
|
||||
server->getDatabaseInterface()->storeGameInformation(room->getName(), _gameTypes, gameInfo, allPlayersEver,
|
||||
allSpectatorsEver, replayList);
|
||||
}
|
||||
}
|
||||
|
||||
void Server_Game::pingClockTimeout()
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
++secondsElapsed;
|
||||
|
||||
GameEventStorage ges;
|
||||
ges.setGameEventContext(Context_PingChanged());
|
||||
|
||||
bool allPlayersInactive = true;
|
||||
int playerCount = 0;
|
||||
for (auto *participant : participants) {
|
||||
if (participant == nullptr)
|
||||
continue;
|
||||
|
||||
if (!participant->getSpectator()) {
|
||||
++playerCount;
|
||||
}
|
||||
|
||||
if (participant->updatePingTime()) {
|
||||
Event_PlayerPropertiesChanged event;
|
||||
event.mutable_player_properties()->set_ping_seconds(participant->getPingTime());
|
||||
ges.enqueueGameEvent(event, participant->getPlayerId());
|
||||
}
|
||||
|
||||
if ((participant->getPingTime() != -1) &&
|
||||
(!participant->getSpectator() || participant->getPlayerId() == hostId)) {
|
||||
allPlayersInactive = false;
|
||||
}
|
||||
}
|
||||
ges.sendToGame(this);
|
||||
|
||||
const int maxTime = room->getServer()->getMaxGameInactivityTime();
|
||||
if (allPlayersInactive) {
|
||||
if (((maxTime > 0) && (++inactivityCounter >= maxTime)) || (playerCount < maxPlayers)) {
|
||||
deleteLater();
|
||||
}
|
||||
} else {
|
||||
inactivityCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
QMap<int, Server_Player *> Server_Game::getPlayers() const // copies pointers to new map
|
||||
{
|
||||
QMap<int, Server_Player *> players;
|
||||
QMutexLocker locker(&gameMutex);
|
||||
for (int id : participants.keys()) {
|
||||
auto *participant = participants[id];
|
||||
if (!participant->getSpectator()) {
|
||||
players[id] = static_cast<Server_Player *>(participant);
|
||||
}
|
||||
}
|
||||
return players;
|
||||
}
|
||||
|
||||
Server_Player *Server_Game::getPlayer(int id) const
|
||||
{
|
||||
auto *participant = participants.value(id);
|
||||
if (!participant->getSpectator()) {
|
||||
return static_cast<Server_Player *>(participant);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int Server_Game::getPlayerCount() const
|
||||
{
|
||||
return participants.size() - getSpectatorCount();
|
||||
}
|
||||
|
||||
int Server_Game::getSpectatorCount() const
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
int result = 0;
|
||||
for (Server_AbstractParticipant *participant : participants.values()) {
|
||||
if (participant->getSpectator())
|
||||
++result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Server_Game::createGameStateChangedEvent(Event_GameStateChanged *event,
|
||||
Server_AbstractParticipant *recipient,
|
||||
bool omniscient,
|
||||
bool withUserInfo)
|
||||
{
|
||||
event->set_seconds_elapsed(secondsElapsed);
|
||||
if (gameStarted) {
|
||||
event->set_game_started(true);
|
||||
event->set_active_player_id(0);
|
||||
event->set_active_phase(0);
|
||||
} else
|
||||
event->set_game_started(false);
|
||||
|
||||
for (Server_AbstractParticipant *participant : participants.values()) {
|
||||
participant->getInfo(event->add_player_list(), recipient, omniscient, withUserInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void Server_Game::sendGameStateToPlayers()
|
||||
{
|
||||
// game state information for replay and omniscient spectators
|
||||
Event_GameStateChanged omniscientEvent;
|
||||
createGameStateChangedEvent(&omniscientEvent, nullptr, true, false);
|
||||
|
||||
GameEventContainer *replayCont = prepareGameEvent(omniscientEvent, -1);
|
||||
replayCont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame);
|
||||
replayCont->clear_game_id();
|
||||
currentReplay->add_event_list()->CopyFrom(*replayCont);
|
||||
delete replayCont;
|
||||
|
||||
// If spectators are not omniscient, we need an additional createGameStateChangedEvent call, otherwise we can use
|
||||
// the data we used for the replay. All spectators are equal, so we don't need to make a createGameStateChangedEvent
|
||||
// call for each one.
|
||||
Event_GameStateChanged spectatorNormalEvent;
|
||||
createGameStateChangedEvent(&spectatorNormalEvent, nullptr, false, false);
|
||||
|
||||
// send game state info to clients according to their role in the game
|
||||
for (auto *participant : participants.values()) {
|
||||
GameEventContainer *gec;
|
||||
if (participant->getSpectator()) {
|
||||
if (spectatorsSeeEverything || participant->getJudge()) {
|
||||
gec = prepareGameEvent(omniscientEvent, -1);
|
||||
} else {
|
||||
gec = prepareGameEvent(spectatorNormalEvent, -1);
|
||||
}
|
||||
} else {
|
||||
Event_GameStateChanged event;
|
||||
createGameStateChangedEvent(&event, participant, false, false);
|
||||
|
||||
gec = prepareGameEvent(event, -1);
|
||||
}
|
||||
participant->sendGameEvent(*gec);
|
||||
delete gec;
|
||||
}
|
||||
}
|
||||
|
||||
void Server_Game::doStartGameIfReady(bool forceStartGame)
|
||||
{
|
||||
Server_DatabaseInterface *databaseInterface = room->getServer()->getDatabaseInterface();
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
if (getPlayerCount() < maxPlayers && !forceStartGame) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto players = getPlayers();
|
||||
for (auto *player : players.values()) {
|
||||
if (!player->getReadyStart()) {
|
||||
if (forceStartGame) {
|
||||
// Player is not ready to start, so kick them
|
||||
// TODO: Move them to Spectators instead
|
||||
kickParticipant(player->getPlayerId());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Server_Player *player : players.values()) {
|
||||
player->setupZones();
|
||||
}
|
||||
|
||||
gameStarted = true;
|
||||
for (auto *player : players.values()) {
|
||||
player->setConceded(false);
|
||||
player->setReadyStart(false);
|
||||
}
|
||||
|
||||
if (firstGameStarted) {
|
||||
currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame);
|
||||
replayList.append(currentReplay);
|
||||
currentReplay = new GameReplay;
|
||||
currentReplay->set_replay_id(databaseInterface->getNextReplayId());
|
||||
ServerInfo_Game *gameInfo = currentReplay->mutable_game_info();
|
||||
getInfo(*gameInfo);
|
||||
gameInfo->set_started(false);
|
||||
|
||||
Event_GameStateChanged omniscientEvent;
|
||||
createGameStateChangedEvent(&omniscientEvent, nullptr, true, true);
|
||||
|
||||
GameEventContainer *replayCont = prepareGameEvent(omniscientEvent, -1);
|
||||
replayCont->set_seconds_elapsed(0);
|
||||
replayCont->clear_game_id();
|
||||
currentReplay->add_event_list()->CopyFrom(*replayCont);
|
||||
delete replayCont;
|
||||
|
||||
startTimeOfThisGame = secondsElapsed;
|
||||
} else
|
||||
firstGameStarted = true;
|
||||
|
||||
sendGameStateToPlayers();
|
||||
|
||||
activePlayer = -1;
|
||||
nextTurn();
|
||||
|
||||
locker.unlock();
|
||||
|
||||
ServerInfo_Game gameInfo;
|
||||
gameInfo.set_room_id(room->getId());
|
||||
gameInfo.set_game_id(gameId);
|
||||
gameInfo.set_started(true);
|
||||
emit gameInfoChanged(gameInfo);
|
||||
}
|
||||
|
||||
void Server_Game::startGameIfReady(bool forceStartGame)
|
||||
{
|
||||
emit sigStartGameIfReady(forceStartGame);
|
||||
}
|
||||
|
||||
void Server_Game::stopGameIfFinished()
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
int playing = 0;
|
||||
auto players = getPlayers();
|
||||
for (auto *player : players.values()) {
|
||||
if (!player->getConceded())
|
||||
++playing;
|
||||
}
|
||||
if (playing > 1)
|
||||
return;
|
||||
|
||||
gameStarted = false;
|
||||
|
||||
for (auto *player : players.values()) {
|
||||
player->clearZones();
|
||||
player->setConceded(false);
|
||||
}
|
||||
|
||||
sendGameStateToPlayers();
|
||||
|
||||
locker.unlock();
|
||||
|
||||
ServerInfo_Game gameInfo;
|
||||
gameInfo.set_room_id(room->getId());
|
||||
gameInfo.set_game_id(gameId);
|
||||
gameInfo.set_started(false);
|
||||
emit gameInfoChanged(gameInfo);
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_Game::checkJoin(ServerInfo_User *user,
|
||||
const QString &_password,
|
||||
bool spectator,
|
||||
bool overrideRestrictions,
|
||||
bool asJudge)
|
||||
{
|
||||
Server_DatabaseInterface *databaseInterface = room->getServer()->getDatabaseInterface();
|
||||
for (auto *participant : participants.values()) {
|
||||
if (participant->getUserInfo()->name() == user->name())
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
if (asJudge && !(user->user_level() & ServerInfo_User::IsJudge)) {
|
||||
return Response::RespUserLevelTooLow;
|
||||
}
|
||||
if (!(overrideRestrictions && (user->user_level() & ServerInfo_User::IsModerator))) {
|
||||
if ((_password != password) && !(spectator && !spectatorsNeedPassword))
|
||||
return Response::RespWrongPassword;
|
||||
if (!(user->user_level() & ServerInfo_User::IsRegistered) && onlyRegistered)
|
||||
return Response::RespUserLevelTooLow;
|
||||
if (onlyBuddies && (user->name() != creatorInfo->name()))
|
||||
if (!databaseInterface->isInBuddyList(QString::fromStdString(creatorInfo->name()),
|
||||
QString::fromStdString(user->name())))
|
||||
return Response::RespOnlyBuddies;
|
||||
if (databaseInterface->isInIgnoreList(QString::fromStdString(creatorInfo->name()),
|
||||
QString::fromStdString(user->name())))
|
||||
return Response::RespInIgnoreList;
|
||||
if (spectator) {
|
||||
if (!spectatorsAllowed)
|
||||
return Response::RespSpectatorsNotAllowed;
|
||||
}
|
||||
}
|
||||
if (!spectator && (gameStarted || (getPlayerCount() >= getMaxPlayers())))
|
||||
return Response::RespGameFull;
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
bool Server_Game::containsUser(const QString &userName) const
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
for (auto *participant : participants.values()) {
|
||||
if (participant->getUserInfo()->name() == userName.toStdString())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Server_Game::addPlayer(Server_AbstractUserInterface *userInterface,
|
||||
ResponseContainer &rc,
|
||||
bool spectator,
|
||||
bool judge,
|
||||
bool broadcastUpdate)
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
Server_AbstractParticipant *newParticipant;
|
||||
if (spectator) {
|
||||
newParticipant = new Server_Spectator(this, nextPlayerId++, userInterface->copyUserInfo(true, true, true),
|
||||
judge, userInterface);
|
||||
} else {
|
||||
newParticipant = new Server_Player(this, nextPlayerId++, userInterface->copyUserInfo(true, true, true), judge,
|
||||
userInterface);
|
||||
}
|
||||
|
||||
newParticipant->moveToThread(thread());
|
||||
|
||||
Event_Join joinEvent;
|
||||
newParticipant->getProperties(*joinEvent.mutable_player_properties(), true);
|
||||
sendGameEventContainer(prepareGameEvent(joinEvent, -1));
|
||||
|
||||
const QString playerName = QString::fromStdString(newParticipant->getUserInfo()->name());
|
||||
participants.insert(newParticipant->getPlayerId(), newParticipant);
|
||||
if (spectator) {
|
||||
allSpectatorsEver.insert(playerName);
|
||||
} else {
|
||||
allPlayersEver.insert(playerName);
|
||||
|
||||
// if the original creator of the game joins, give them host status back
|
||||
// FIXME: transferring host to spectators has side effects
|
||||
if (newParticipant->getUserInfo()->name() == creatorInfo->name()) {
|
||||
hostId = newParticipant->getPlayerId();
|
||||
sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId));
|
||||
}
|
||||
}
|
||||
|
||||
if (broadcastUpdate) {
|
||||
ServerInfo_Game gameInfo;
|
||||
gameInfo.set_room_id(room->getId());
|
||||
gameInfo.set_game_id(gameId);
|
||||
gameInfo.set_player_count(getPlayerCount());
|
||||
gameInfo.set_spectators_count(getSpectatorCount());
|
||||
emit gameInfoChanged(gameInfo);
|
||||
}
|
||||
|
||||
if ((newParticipant->getUserInfo()->user_level() & ServerInfo_User::IsRegistered) && !spectator)
|
||||
room->getServer()->addPersistentPlayer(playerName, room->getId(), gameId, newParticipant->getPlayerId());
|
||||
|
||||
userInterface->playerAddedToGame(gameId, room->getId(), newParticipant->getPlayerId());
|
||||
|
||||
createGameJoinedEvent(newParticipant, rc, false);
|
||||
}
|
||||
|
||||
void Server_Game::removeParticipant(Server_AbstractParticipant *participant, Event_Leave::LeaveReason reason)
|
||||
{
|
||||
room->getServer()->removePersistentPlayer(QString::fromStdString(participant->getUserInfo()->name()), room->getId(),
|
||||
gameId, participant->getPlayerId());
|
||||
participants.remove(participant->getPlayerId());
|
||||
|
||||
bool spectator = participant->getSpectator();
|
||||
GameEventStorage ges;
|
||||
if (!spectator) {
|
||||
auto *player = static_cast<Server_Player *>(participant);
|
||||
removeArrowsRelatedToPlayer(ges, player);
|
||||
unattachCards(ges, player);
|
||||
}
|
||||
|
||||
Event_Leave event;
|
||||
event.set_reason(reason);
|
||||
ges.enqueueGameEvent(event, participant->getPlayerId());
|
||||
ges.sendToGame(this);
|
||||
|
||||
bool playerActive = activePlayer == participant->getPlayerId();
|
||||
bool playerHost = hostId == participant->getPlayerId();
|
||||
participant->prepareDestroy();
|
||||
|
||||
if (playerHost) {
|
||||
int newHostId = -1;
|
||||
for (auto *otherPlayer : getPlayers().values()) {
|
||||
newHostId = otherPlayer->getPlayerId();
|
||||
break;
|
||||
}
|
||||
if (newHostId != -1) {
|
||||
hostId = newHostId;
|
||||
sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId));
|
||||
} else {
|
||||
gameClosed = true;
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!spectator) {
|
||||
stopGameIfFinished();
|
||||
if (gameStarted && playerActive)
|
||||
nextTurn();
|
||||
}
|
||||
|
||||
ServerInfo_Game gameInfo;
|
||||
gameInfo.set_room_id(room->getId());
|
||||
gameInfo.set_game_id(gameId);
|
||||
gameInfo.set_player_count(getPlayerCount());
|
||||
gameInfo.set_spectators_count(getSpectatorCount());
|
||||
emit gameInfoChanged(gameInfo);
|
||||
}
|
||||
|
||||
void Server_Game::removeArrowsRelatedToPlayer(GameEventStorage &ges, Server_Player *player)
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
// Remove all arrows of other players pointing to the player being removed or to one of his cards.
|
||||
// Also remove all arrows starting at one of his cards. This is necessary since players can create
|
||||
// arrows that start at another person's cards.
|
||||
for (Server_Player *anyPlayer : getPlayers().values()) {
|
||||
QList<Server_Arrow *> arrows = anyPlayer->getArrows().values();
|
||||
QList<Server_Arrow *> toDelete;
|
||||
for (int i = 0; i < arrows.size(); ++i) {
|
||||
Server_Arrow *arrow = arrows[i];
|
||||
Server_Card *targetCard = qobject_cast<Server_Card *>(arrow->getTargetItem());
|
||||
if (targetCard) {
|
||||
if (targetCard->getZone() != nullptr && targetCard->getZone()->getPlayer() == player)
|
||||
toDelete.append(arrow);
|
||||
} else if (static_cast<Server_Player *>(arrow->getTargetItem()) == player)
|
||||
toDelete.append(arrow);
|
||||
|
||||
// Don't use else here! It has to happen regardless of whether targetCard == 0.
|
||||
if (arrow->getStartCard()->getZone() != nullptr && arrow->getStartCard()->getZone()->getPlayer() == player)
|
||||
toDelete.append(arrow);
|
||||
}
|
||||
for (int i = 0; i < toDelete.size(); ++i) {
|
||||
Event_DeleteArrow event;
|
||||
event.set_arrow_id(toDelete[i]->getId());
|
||||
ges.enqueueGameEvent(event, player->getPlayerId());
|
||||
|
||||
player->deleteArrow(toDelete[i]->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server_Game::unattachCards(GameEventStorage &ges, Server_Player *player)
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
for (auto zone : player->getZones()) {
|
||||
for (auto card : zone->getCards()) {
|
||||
// Make a copy of the list because the original one gets modified during the loop
|
||||
QList<Server_Card *> attachedCards = card->getAttachedCards();
|
||||
for (Server_Card *attachedCard : attachedCards) {
|
||||
auto otherPlayer = attachedCard->getZone()->getPlayer();
|
||||
// do not modify the current player's zone!
|
||||
// this would cause the current card iterator to be invalidated!
|
||||
// we only have to return cards owned by other players
|
||||
// because the current player is leaving the game anyway
|
||||
if (otherPlayer != player) {
|
||||
otherPlayer->unattachCard(ges, attachedCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Server_Game::kickParticipant(int playerId)
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
auto *participant = participants.value(playerId);
|
||||
if (!participant)
|
||||
return false;
|
||||
|
||||
GameEventContainer *gec = prepareGameEvent(Event_Kicked(), -1);
|
||||
participant->sendGameEvent(*gec);
|
||||
delete gec;
|
||||
|
||||
removeParticipant(participant, Event_Leave::USER_KICKED);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Server_Game::setActivePlayer(int _activePlayer)
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
activePlayer = _activePlayer;
|
||||
|
||||
Event_SetActivePlayer event;
|
||||
event.set_active_player_id(activePlayer);
|
||||
sendGameEventContainer(prepareGameEvent(event, -1));
|
||||
|
||||
setActivePhase(0);
|
||||
}
|
||||
|
||||
void Server_Game::setActivePhase(int _activePhase)
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
for (auto *player : getPlayers().values()) {
|
||||
QList<Server_Arrow *> toDelete = player->getArrows().values();
|
||||
for (int i = 0; i < toDelete.size(); ++i) {
|
||||
Server_Arrow *a = toDelete[i];
|
||||
|
||||
Event_DeleteArrow event;
|
||||
event.set_arrow_id(a->getId());
|
||||
sendGameEventContainer(prepareGameEvent(event, player->getPlayerId()));
|
||||
|
||||
player->deleteArrow(a->getId());
|
||||
}
|
||||
}
|
||||
|
||||
activePhase = _activePhase;
|
||||
|
||||
Event_SetActivePhase event;
|
||||
event.set_phase(activePhase);
|
||||
sendGameEventContainer(prepareGameEvent(event, -1));
|
||||
}
|
||||
|
||||
void Server_Game::nextTurn()
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
if (participants.isEmpty()) {
|
||||
qWarning() << "Server_Game::nextTurn was called while players is empty; gameId = " << gameId;
|
||||
return;
|
||||
}
|
||||
|
||||
auto players = getPlayers();
|
||||
const QList<int> keys = players.keys();
|
||||
int listPos = -1;
|
||||
if (activePlayer != -1) {
|
||||
listPos = keys.indexOf(activePlayer);
|
||||
}
|
||||
do {
|
||||
if (turnOrderReversed) {
|
||||
--listPos;
|
||||
if (listPos < 0) {
|
||||
listPos = keys.size() - 1;
|
||||
}
|
||||
} else {
|
||||
++listPos;
|
||||
if (listPos == keys.size()) {
|
||||
listPos = 0;
|
||||
}
|
||||
}
|
||||
} while (players.value(keys[listPos])->getConceded());
|
||||
|
||||
setActivePlayer(keys[listPos]);
|
||||
}
|
||||
|
||||
void Server_Game::createGameJoinedEvent(Server_AbstractParticipant *joiningParticipant,
|
||||
ResponseContainer &rc,
|
||||
bool resuming)
|
||||
{
|
||||
Event_GameJoined event1;
|
||||
getInfo(*event1.mutable_game_info());
|
||||
event1.set_host_id(hostId);
|
||||
event1.set_player_id(joiningParticipant->getPlayerId());
|
||||
event1.set_spectator(joiningParticipant->getSpectator());
|
||||
event1.set_judge(joiningParticipant->getJudge());
|
||||
event1.set_resuming(resuming);
|
||||
if (resuming) {
|
||||
const QStringList &allGameTypes = room->getGameTypes();
|
||||
for (int i = 0; i < allGameTypes.size(); ++i) {
|
||||
ServerInfo_GameType *newGameType = event1.add_game_types();
|
||||
newGameType->set_game_type_id(i);
|
||||
newGameType->set_description(allGameTypes[i].toStdString());
|
||||
}
|
||||
}
|
||||
rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, Server_AbstractUserInterface::prepareSessionEvent(event1));
|
||||
|
||||
Event_GameStateChanged event2;
|
||||
event2.set_seconds_elapsed(secondsElapsed);
|
||||
event2.set_game_started(gameStarted);
|
||||
event2.set_active_player_id(activePlayer);
|
||||
event2.set_active_phase(activePhase);
|
||||
|
||||
bool omniscient = joiningParticipant->getSpectator() && (spectatorsSeeEverything || joiningParticipant->getJudge());
|
||||
for (auto *participant : participants.values()) {
|
||||
participant->getInfo(event2.add_player_list(), joiningParticipant, omniscient, true);
|
||||
}
|
||||
|
||||
rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, prepareGameEvent(event2, -1));
|
||||
}
|
||||
|
||||
void Server_Game::sendGameEventContainer(GameEventContainer *cont,
|
||||
GameEventStorageItem::EventRecipients recipients,
|
||||
int privatePlayerId)
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
cont->set_game_id(gameId);
|
||||
for (auto *participant : participants.values()) {
|
||||
const bool playerPrivate =
|
||||
(participant->getPlayerId() == privatePlayerId) ||
|
||||
(participant->getSpectator() && (spectatorsSeeEverything || participant->getJudge()));
|
||||
if ((recipients.testFlag(GameEventStorageItem::SendToPrivate) && playerPrivate) ||
|
||||
(recipients.testFlag(GameEventStorageItem::SendToOthers) && !playerPrivate))
|
||||
participant->sendGameEvent(*cont);
|
||||
}
|
||||
if (recipients.testFlag(GameEventStorageItem::SendToPrivate)) {
|
||||
cont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame);
|
||||
cont->clear_game_id();
|
||||
currentReplay->add_event_list()->CopyFrom(*cont);
|
||||
}
|
||||
|
||||
delete cont;
|
||||
}
|
||||
|
||||
GameEventContainer *
|
||||
Server_Game::prepareGameEvent(const ::google::protobuf::Message &gameEvent, int playerId, GameEventContext *context)
|
||||
{
|
||||
GameEventContainer *cont = new GameEventContainer;
|
||||
cont->set_game_id(gameId);
|
||||
if (context)
|
||||
cont->mutable_context()->CopyFrom(*context);
|
||||
GameEvent *event = cont->add_event_list();
|
||||
if (playerId != -1)
|
||||
event->set_player_id(playerId);
|
||||
event->GetReflection()
|
||||
->MutableMessage(event, gameEvent.GetDescriptor()->FindExtensionByName("ext"))
|
||||
->CopyFrom(gameEvent);
|
||||
return cont;
|
||||
}
|
||||
|
||||
void Server_Game::getInfo(ServerInfo_Game &result) const
|
||||
{
|
||||
QMutexLocker locker(&gameMutex);
|
||||
|
||||
result.set_room_id(room->getId());
|
||||
result.set_game_id(gameId);
|
||||
if (gameClosed) {
|
||||
result.set_closed(true);
|
||||
} else {
|
||||
for (auto type : gameTypes) {
|
||||
result.add_game_types(type);
|
||||
}
|
||||
|
||||
result.set_max_players(getMaxPlayers());
|
||||
result.set_description(getDescription().toStdString());
|
||||
result.set_with_password(!getPassword().isEmpty());
|
||||
result.set_player_count(getPlayerCount());
|
||||
result.set_started(gameStarted);
|
||||
result.mutable_creator_info()->CopyFrom(*getCreatorInfo());
|
||||
result.set_only_buddies(onlyBuddies);
|
||||
result.set_only_registered(onlyRegistered);
|
||||
result.set_spectators_allowed(getSpectatorsAllowed());
|
||||
result.set_spectators_need_password(getSpectatorsNeedPassword());
|
||||
result.set_spectators_can_chat(spectatorsCanTalk);
|
||||
result.set_spectators_omniscient(spectatorsSeeEverything);
|
||||
result.set_share_decklists_on_load(shareDecklistsOnLoad);
|
||||
result.set_spectators_count(getSpectatorCount());
|
||||
result.set_start_time(startTime.toSecsSinceEpoch());
|
||||
}
|
||||
}
|
||||
226
common/server/game/server_game.h
Normal file
226
common/server/game/server_game.h
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Max-Wilhelm Bruker *
|
||||
* brukie@laptop *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#ifndef SERVERGAME_H
|
||||
#define SERVERGAME_H
|
||||
|
||||
#include "../server_response_containers.h"
|
||||
#include "pb/event_leave.pb.h"
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/serverinfo_game.pb.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
|
||||
class QTimer;
|
||||
class GameEventContainer;
|
||||
class GameReplay;
|
||||
class Server_Room;
|
||||
class Server_Player;
|
||||
class Server_AbstractParticipant;
|
||||
class ServerInfo_User;
|
||||
class ServerInfo_Game;
|
||||
class Server_AbstractUserInterface;
|
||||
class Event_GameStateChanged;
|
||||
|
||||
class Server_Game : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
Server_Room *room;
|
||||
int nextPlayerId;
|
||||
int hostId;
|
||||
ServerInfo_User *creatorInfo;
|
||||
QMap<int, Server_AbstractParticipant *> participants;
|
||||
QSet<QString> allPlayersEver, allSpectatorsEver;
|
||||
bool gameStarted;
|
||||
bool gameClosed;
|
||||
int gameId;
|
||||
QString description;
|
||||
QString password;
|
||||
int maxPlayers;
|
||||
QList<int> gameTypes;
|
||||
int activePlayer, activePhase;
|
||||
bool onlyBuddies, onlyRegistered;
|
||||
bool spectatorsAllowed;
|
||||
bool spectatorsNeedPassword;
|
||||
bool spectatorsCanTalk;
|
||||
bool spectatorsSeeEverything;
|
||||
int startingLifeTotal;
|
||||
bool shareDecklistsOnLoad;
|
||||
int inactivityCounter;
|
||||
int startTimeOfThisGame, secondsElapsed;
|
||||
bool firstGameStarted;
|
||||
bool turnOrderReversed;
|
||||
QDateTime startTime;
|
||||
QTimer *pingClock;
|
||||
QList<GameReplay *> replayList;
|
||||
GameReplay *currentReplay;
|
||||
|
||||
void createGameStateChangedEvent(Event_GameStateChanged *event,
|
||||
Server_AbstractParticipant *recipient,
|
||||
bool omniscient,
|
||||
bool withUserInfo);
|
||||
void storeGameInformation();
|
||||
signals:
|
||||
void sigStartGameIfReady(bool override);
|
||||
void gameInfoChanged(ServerInfo_Game gameInfo);
|
||||
private slots:
|
||||
void pingClockTimeout();
|
||||
void doStartGameIfReady(bool forceStartGame = false);
|
||||
|
||||
public:
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
mutable QRecursiveMutex gameMutex;
|
||||
#else
|
||||
mutable QMutex gameMutex;
|
||||
#endif
|
||||
Server_Game(const ServerInfo_User &_creatorInfo,
|
||||
int _gameId,
|
||||
const QString &_description,
|
||||
const QString &_password,
|
||||
int _maxPlayers,
|
||||
const QList<int> &_gameTypes,
|
||||
bool _onlyBuddies,
|
||||
bool _onlyRegistered,
|
||||
bool _spectatorsAllowed,
|
||||
bool _spectatorsNeedPassword,
|
||||
bool _spectatorsCanTalk,
|
||||
bool _spectatorsSeeEverything,
|
||||
int _startingLifeTotal,
|
||||
bool _shareDecklistsOnLoad,
|
||||
Server_Room *parent);
|
||||
~Server_Game() override;
|
||||
Server_Room *getRoom() const
|
||||
{
|
||||
return room;
|
||||
}
|
||||
void getInfo(ServerInfo_Game &result) const;
|
||||
int getHostId() const
|
||||
{
|
||||
return hostId;
|
||||
}
|
||||
ServerInfo_User *getCreatorInfo() const
|
||||
{
|
||||
return creatorInfo;
|
||||
}
|
||||
bool getGameStarted() const
|
||||
{
|
||||
return gameStarted;
|
||||
}
|
||||
int getPlayerCount() const;
|
||||
int getSpectatorCount() const;
|
||||
QMap<int, Server_Player *> getPlayers() const;
|
||||
Server_Player *getPlayer(int id) const;
|
||||
const QMap<int, Server_AbstractParticipant *> &getParticipants() const
|
||||
{
|
||||
return participants;
|
||||
}
|
||||
int getGameId() const
|
||||
{
|
||||
return gameId;
|
||||
}
|
||||
QString getDescription() const
|
||||
{
|
||||
return description;
|
||||
}
|
||||
QString getPassword() const
|
||||
{
|
||||
return password;
|
||||
}
|
||||
int getMaxPlayers() const
|
||||
{
|
||||
return maxPlayers;
|
||||
}
|
||||
bool getSpectatorsAllowed() const
|
||||
{
|
||||
return spectatorsAllowed;
|
||||
}
|
||||
bool getSpectatorsNeedPassword() const
|
||||
{
|
||||
return spectatorsNeedPassword;
|
||||
}
|
||||
bool getSpectatorsCanTalk() const
|
||||
{
|
||||
return spectatorsCanTalk;
|
||||
}
|
||||
bool getSpectatorsSeeEverything() const
|
||||
{
|
||||
return spectatorsSeeEverything;
|
||||
}
|
||||
int getStartingLifeTotal() const
|
||||
{
|
||||
return startingLifeTotal;
|
||||
}
|
||||
bool getShareDecklistsOnLoad() const
|
||||
{
|
||||
return shareDecklistsOnLoad;
|
||||
}
|
||||
Response::ResponseCode
|
||||
checkJoin(ServerInfo_User *user, const QString &_password, bool spectator, bool overrideRestrictions, bool asJudge);
|
||||
bool containsUser(const QString &userName) const;
|
||||
void addPlayer(Server_AbstractUserInterface *userInterface,
|
||||
ResponseContainer &rc,
|
||||
bool spectator,
|
||||
bool judge,
|
||||
bool broadcastUpdate = true);
|
||||
void removeParticipant(Server_AbstractParticipant *participant, Event_Leave::LeaveReason reason);
|
||||
void removeArrowsRelatedToPlayer(GameEventStorage &ges, Server_Player *player);
|
||||
void unattachCards(GameEventStorage &ges, Server_Player *player);
|
||||
bool kickParticipant(int playerId);
|
||||
void startGameIfReady(bool forceStartGame);
|
||||
void stopGameIfFinished();
|
||||
int getActivePlayer() const
|
||||
{
|
||||
return activePlayer;
|
||||
}
|
||||
int getActivePhase() const
|
||||
{
|
||||
return activePhase;
|
||||
}
|
||||
void setActivePlayer(int _activePlayer);
|
||||
void setActivePhase(int _activePhase);
|
||||
void nextTurn();
|
||||
int getSecondsElapsed() const
|
||||
{
|
||||
return secondsElapsed;
|
||||
}
|
||||
bool reverseTurnOrder()
|
||||
{
|
||||
return turnOrderReversed = !turnOrderReversed;
|
||||
}
|
||||
|
||||
void createGameJoinedEvent(Server_AbstractParticipant *participant, ResponseContainer &rc, bool resuming);
|
||||
|
||||
GameEventContainer *
|
||||
prepareGameEvent(const ::google::protobuf::Message &gameEvent, int playerId, GameEventContext *context = 0);
|
||||
GameEventContext prepareGameEventContext(const ::google::protobuf::Message &gameEventContext);
|
||||
|
||||
void sendGameStateToPlayers();
|
||||
void sendGameEventContainer(GameEventContainer *cont,
|
||||
GameEventStorageItem::EventRecipients recipients = GameEventStorageItem::SendToPrivate |
|
||||
GameEventStorageItem::SendToOthers,
|
||||
int privatePlayerId = -1);
|
||||
};
|
||||
|
||||
#endif
|
||||
2146
common/server/game/server_player.cpp
Normal file
2146
common/server/game/server_player.cpp
Normal file
File diff suppressed because it is too large
Load diff
178
common/server/game/server_player.h
Normal file
178
common/server/game/server_player.h
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#ifndef PLAYER_H
|
||||
#define PLAYER_H
|
||||
|
||||
#include "../../serverinfo_user_container.h"
|
||||
#include "server_abstract_participant.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
class DeckList;
|
||||
class Server_CardZone;
|
||||
class Server_Counter;
|
||||
class Server_Arrow;
|
||||
class Server_Card;
|
||||
class CardToMove;
|
||||
|
||||
class Server_Player : public Server_AbstractParticipant
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
class MoveCardCompareFunctor;
|
||||
DeckList *deck;
|
||||
QMap<QString, Server_CardZone *> zones;
|
||||
QMap<int, Server_Counter *> counters;
|
||||
QMap<int, Server_Arrow *> arrows;
|
||||
QList<int> lastDrawList;
|
||||
int nextCardId;
|
||||
bool readyStart;
|
||||
bool conceded;
|
||||
bool sideboardLocked;
|
||||
void revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorage &ges);
|
||||
void sendCreateTokenEvents(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord, GameEventStorage &ges);
|
||||
void getPlayerProperties(ServerInfo_PlayerProperties &result) override;
|
||||
|
||||
public:
|
||||
Server_Player(Server_Game *_game,
|
||||
int _playerId,
|
||||
const ServerInfo_User &_userInfo,
|
||||
bool _judge,
|
||||
Server_AbstractUserInterface *_handler);
|
||||
~Server_Player() override;
|
||||
void prepareDestroy() override;
|
||||
const DeckList *getDeckList() const
|
||||
{
|
||||
return deck;
|
||||
}
|
||||
bool getReadyStart() const
|
||||
{
|
||||
return readyStart;
|
||||
}
|
||||
void setReadyStart(bool _readyStart)
|
||||
{
|
||||
readyStart = _readyStart;
|
||||
}
|
||||
bool getConceded() const
|
||||
{
|
||||
return conceded;
|
||||
}
|
||||
void setConceded(bool _conceded)
|
||||
{
|
||||
conceded = _conceded;
|
||||
}
|
||||
|
||||
const QMap<QString, Server_CardZone *> &getZones() const
|
||||
{
|
||||
return zones;
|
||||
}
|
||||
const QMap<int, Server_Counter *> &getCounters() const
|
||||
{
|
||||
return counters;
|
||||
}
|
||||
const QMap<int, Server_Arrow *> &getArrows() const
|
||||
{
|
||||
return arrows;
|
||||
}
|
||||
|
||||
int newCardId();
|
||||
int newCounterId() const;
|
||||
int newArrowId() const;
|
||||
|
||||
void addZone(Server_CardZone *zone);
|
||||
void addArrow(Server_Arrow *arrow);
|
||||
void updateArrowId(int id);
|
||||
bool deleteArrow(int arrowId);
|
||||
void addCounter(Server_Counter *counter);
|
||||
|
||||
void clearZones();
|
||||
void setupZones();
|
||||
|
||||
Response::ResponseCode drawCards(GameEventStorage &ges, int number);
|
||||
Response::ResponseCode moveCard(GameEventStorage &ges,
|
||||
Server_CardZone *startzone,
|
||||
const QList<const CardToMove *> &_cards,
|
||||
Server_CardZone *targetzone,
|
||||
int xCoord,
|
||||
int yCoord,
|
||||
bool fixFreeSpaces = true,
|
||||
bool undoingDraw = false,
|
||||
bool isReversed = false);
|
||||
void unattachCard(GameEventStorage &ges, Server_Card *card);
|
||||
Response::ResponseCode setCardAttrHelper(GameEventStorage &ges,
|
||||
int targetPlayerId,
|
||||
const QString &zone,
|
||||
int cardId,
|
||||
CardAttribute attribute,
|
||||
const QString &attrValue,
|
||||
Server_Card *unzonedCard = nullptr);
|
||||
|
||||
Response::ResponseCode
|
||||
cmdConcede(const Command_Concede &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdUnconcede(const Command_Unconcede &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdSetSideboardLock(const Command_SetSideboardLock &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdShuffle(const Command_Shuffle &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdMulligan(const Command_Mulligan &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdRollDie(const Command_RollDie &cmd, ResponseContainer &rc, GameEventStorage &ges) const override;
|
||||
Response::ResponseCode
|
||||
cmdDrawCards(const Command_DrawCards &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdUndoDraw(const Command_UndoDraw &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdMoveCard(const Command_MoveCard &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdFlipCard(const Command_FlipCard &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdDeleteArrow(const Command_DeleteArrow &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdSetCardAttr(const Command_SetCardAttr &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdSetCardCounter(const Command_SetCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdIncCardCounter(const Command_IncCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdNextTurn(const Command_NextTurn &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdDumpZone(const Command_DumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdRevealCards(const Command_RevealCards &cmd, ResponseContainer &rc, GameEventStorage &ges) override;
|
||||
Response::ResponseCode
|
||||
cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges) override;
|
||||
Response::ResponseCode cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd,
|
||||
ResponseContainer &rc,
|
||||
GameEventStorage &ges) override;
|
||||
|
||||
void getInfo(ServerInfo_Player *info,
|
||||
Server_AbstractParticipant *playerWhosAsking,
|
||||
bool omniscient,
|
||||
bool withUserInfo) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
11
common/server/game/server_spectator.cpp
Normal file
11
common/server/game/server_spectator.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#include "server_spectator.h"
|
||||
|
||||
Server_Spectator::Server_Spectator(Server_Game *_game,
|
||||
int _playerId,
|
||||
const ServerInfo_User &_userInfo,
|
||||
bool _judge,
|
||||
Server_AbstractUserInterface *_userInterface)
|
||||
: Server_AbstractParticipant(_game, _playerId, _userInfo, _judge, _userInterface)
|
||||
{
|
||||
spectator = true;
|
||||
}
|
||||
17
common/server/game/server_spectator.h
Normal file
17
common/server/game/server_spectator.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef SPECTATOR_H
|
||||
#define SPECTATOR_H
|
||||
|
||||
#include "server_abstract_participant.h"
|
||||
|
||||
class Server_Spectator : public Server_AbstractParticipant
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Server_Spectator(Server_Game *_game,
|
||||
int _playerId,
|
||||
const ServerInfo_User &_userInfo,
|
||||
bool _judge,
|
||||
Server_AbstractUserInterface *_handler);
|
||||
};
|
||||
|
||||
#endif
|
||||
660
common/server/server.cpp
Normal file
660
common/server/server.cpp
Normal file
|
|
@ -0,0 +1,660 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Max-Wilhelm Bruker *
|
||||
* brukie@laptop *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#include "server.h"
|
||||
|
||||
#include "../debug_pb_message.h"
|
||||
#include "../featureset.h"
|
||||
#include "game/server_game.h"
|
||||
#include "game/server_player.h"
|
||||
#include "pb/event_connection_closed.pb.h"
|
||||
#include "pb/event_list_rooms.pb.h"
|
||||
#include "pb/event_user_joined.pb.h"
|
||||
#include "pb/event_user_left.pb.h"
|
||||
#include "pb/isl_message.pb.h"
|
||||
#include "pb/session_event.pb.h"
|
||||
#include "server_database_interface.h"
|
||||
#include "server_protocolhandler.h"
|
||||
#include "server_remoteuserinterface.h"
|
||||
#include "server_room.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
|
||||
Server::Server(QObject *parent) : QObject(parent), nextLocalGameId(0), tcpUserCount(0), webSocketUserCount(0)
|
||||
{
|
||||
qRegisterMetaType<ServerInfo_Ban>("ServerInfo_Ban");
|
||||
qRegisterMetaType<ServerInfo_Game>("ServerInfo_Game");
|
||||
qRegisterMetaType<ServerInfo_Room>("ServerInfo_Room");
|
||||
qRegisterMetaType<ServerInfo_User>("ServerInfo_User");
|
||||
qRegisterMetaType<CommandContainer>("CommandContainer");
|
||||
qRegisterMetaType<Response>("Response");
|
||||
qRegisterMetaType<GameEventContainer>("GameEventContainer");
|
||||
qRegisterMetaType<IslMessage>("IslMessage");
|
||||
qRegisterMetaType<Command_JoinGame>("Command_JoinGame");
|
||||
|
||||
connect(this, &Server::sigSendIslMessage, this, &Server::doSendIslMessage, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void Server::prepareDestroy()
|
||||
{
|
||||
roomsLock.lockForWrite();
|
||||
QMapIterator<int, Server_Room *> roomIterator(rooms);
|
||||
while (roomIterator.hasNext())
|
||||
delete roomIterator.next().value();
|
||||
rooms.clear();
|
||||
roomsLock.unlock();
|
||||
}
|
||||
|
||||
void Server::setDatabaseInterface(Server_DatabaseInterface *_databaseInterface)
|
||||
{
|
||||
connect(this, &Server::endSession, _databaseInterface, &Server_DatabaseInterface::endSession);
|
||||
databaseInterfaces.insert(QThread::currentThread(), _databaseInterface);
|
||||
}
|
||||
|
||||
Server_DatabaseInterface *Server::getDatabaseInterface() const
|
||||
{
|
||||
return databaseInterfaces.value(QThread::currentThread());
|
||||
}
|
||||
|
||||
AuthenticationResult Server::loginUser(Server_ProtocolHandler *session,
|
||||
QString &name,
|
||||
const QString &password,
|
||||
bool passwordNeedsHash,
|
||||
QString &reasonStr,
|
||||
int &secondsLeft,
|
||||
QString &clientid,
|
||||
QString &clientVersion,
|
||||
QString & /* connectionType */)
|
||||
{
|
||||
bool hasClientId = false;
|
||||
if (clientid.isEmpty()) {
|
||||
// client id is empty, either out dated client or client has been modified
|
||||
if (getClientIDRequiredEnabled())
|
||||
return ClientIdRequired;
|
||||
} else {
|
||||
hasClientId = true;
|
||||
}
|
||||
|
||||
if (name.size() > 35)
|
||||
name = name.left(35);
|
||||
|
||||
Server_DatabaseInterface *databaseInterface = getDatabaseInterface();
|
||||
|
||||
AuthenticationResult authState = databaseInterface->checkUserPassword(session, name, password, clientid, reasonStr,
|
||||
secondsLeft, passwordNeedsHash);
|
||||
if (authState == NotLoggedIn || authState == UserIsBanned || authState == UsernameInvalid ||
|
||||
authState == UserIsInactive)
|
||||
return authState;
|
||||
|
||||
ServerInfo_User data = databaseInterface->getUserData(name, true);
|
||||
data.set_address(session->getAddress().toStdString());
|
||||
name = QString::fromStdString(data.name()); // Compensate for case indifference
|
||||
|
||||
if (authState == PasswordRight) {
|
||||
if (users.contains(name) || databaseInterface->userSessionExists(name)) {
|
||||
if (users.contains(name)) {
|
||||
qDebug("Session already logged in, logging old session out");
|
||||
Event_ConnectionClosed event;
|
||||
event.set_reason(Event_ConnectionClosed::LOGGEDINELSEWERE);
|
||||
event.set_reason_str("You have been logged out due to logging in at another location.");
|
||||
event.set_end_time(QDateTime::currentDateTime().toSecsSinceEpoch());
|
||||
|
||||
SessionEvent *se = users.value(name)->prepareSessionEvent(event);
|
||||
users.value(name)->sendProtocolItem(*se);
|
||||
delete se;
|
||||
|
||||
users.value(name)->prepareDestroy();
|
||||
} else {
|
||||
qDebug() << "Active session and sessions table inconsistent, please validate session table information "
|
||||
"for user "
|
||||
<< name;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (authState == UnknownUser) {
|
||||
// Change user name so that no two users have the same names,
|
||||
// don't interfere with registered user names though.
|
||||
if (getRegOnlyServerEnabled()) {
|
||||
qDebug("Login denied: registration required");
|
||||
databaseInterface->unlockSessionTables();
|
||||
return RegistrationRequired;
|
||||
}
|
||||
|
||||
QString tempName = name;
|
||||
int i = 0;
|
||||
while (users.contains(tempName) || databaseInterface->activeUserExists(tempName) ||
|
||||
databaseInterface->userSessionExists(tempName))
|
||||
tempName = name + "_" + QString::number(++i);
|
||||
name = tempName;
|
||||
data.set_name(name.toStdString());
|
||||
}
|
||||
|
||||
QWriteLocker locker(&clientsLock);
|
||||
databaseInterface->lockSessionTables();
|
||||
users.insert(name, session);
|
||||
qDebug() << "Server::loginUser:" << session << "name=" << name;
|
||||
|
||||
data.set_session_id(static_cast<google::protobuf::uint64>(
|
||||
databaseInterface->startSession(name, session->getAddress(), clientid, session->getConnectionType())));
|
||||
databaseInterface->unlockSessionTables();
|
||||
|
||||
usersBySessionId.insert(data.session_id(), session);
|
||||
|
||||
qDebug() << "session id:" << data.session_id();
|
||||
session->setUserInfo(data);
|
||||
|
||||
Event_UserJoined event;
|
||||
event.mutable_user_info()->CopyFrom(session->copyUserInfo(false));
|
||||
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
for (auto &client : clients)
|
||||
if (client->getAcceptsUserListChanges())
|
||||
client->sendProtocolItem(*se);
|
||||
delete se;
|
||||
|
||||
event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true, true));
|
||||
locker.unlock();
|
||||
|
||||
if (hasClientId) {
|
||||
// update users database table with client id
|
||||
databaseInterface->updateUsersClientID(name, clientid);
|
||||
}
|
||||
databaseInterface->updateUsersLastLoginData(name, clientVersion);
|
||||
se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
sendIsl_SessionEvent(*se);
|
||||
delete se;
|
||||
|
||||
return authState;
|
||||
}
|
||||
|
||||
void Server::addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId)
|
||||
{
|
||||
QWriteLocker locker(&persistentPlayersLock);
|
||||
persistentPlayers.insert(userName, PlayerReference(roomId, gameId, playerId));
|
||||
}
|
||||
|
||||
void Server::removePersistentPlayer(const QString &userName, int roomId, int gameId, int playerId)
|
||||
{
|
||||
QWriteLocker locker(&persistentPlayersLock);
|
||||
persistentPlayers.remove(userName, PlayerReference(roomId, gameId, playerId));
|
||||
}
|
||||
|
||||
QList<PlayerReference> Server::getPersistentPlayerReferences(const QString &userName) const
|
||||
{
|
||||
QReadLocker locker(&persistentPlayersLock);
|
||||
return persistentPlayers.values(userName);
|
||||
}
|
||||
|
||||
Server_AbstractUserInterface *Server::findUser(const QString &userName) const
|
||||
{
|
||||
// Call this only with clientsLock set.
|
||||
|
||||
Server_AbstractUserInterface *userHandler = users.value(userName);
|
||||
if (userHandler)
|
||||
return userHandler;
|
||||
else
|
||||
return externalUsers.value(userName);
|
||||
}
|
||||
|
||||
void Server::addClient(Server_ProtocolHandler *client)
|
||||
{
|
||||
if (client->getConnectionType() == "tcp")
|
||||
tcpUserCount++;
|
||||
|
||||
if (client->getConnectionType() == "websocket")
|
||||
webSocketUserCount++;
|
||||
|
||||
QWriteLocker locker(&clientsLock);
|
||||
clients << client;
|
||||
}
|
||||
|
||||
void Server::removeClient(Server_ProtocolHandler *client)
|
||||
{
|
||||
int clientIndex = clients.indexOf(client);
|
||||
if (clientIndex == -1) {
|
||||
qWarning() << "tried to remove non existing client";
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->getConnectionType() == "tcp")
|
||||
tcpUserCount--;
|
||||
|
||||
if (client->getConnectionType() == "websocket")
|
||||
webSocketUserCount--;
|
||||
|
||||
QWriteLocker locker(&clientsLock);
|
||||
clients.removeAt(clientIndex);
|
||||
ServerInfo_User *data = client->getUserInfo();
|
||||
if (data) {
|
||||
Event_UserLeft event;
|
||||
event.set_name(data->name());
|
||||
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
for (auto &_client : clients)
|
||||
if (_client->getAcceptsUserListChanges())
|
||||
_client->sendProtocolItem(*se);
|
||||
sendIsl_SessionEvent(*se);
|
||||
delete se;
|
||||
|
||||
users.remove(QString::fromStdString(data->name()));
|
||||
qDebug() << "Server::removeClient: name=" << QString::fromStdString(data->name());
|
||||
|
||||
if (data->has_session_id()) {
|
||||
const qint64 sessionId = data->session_id();
|
||||
usersBySessionId.remove(sessionId);
|
||||
emit endSession(sessionId);
|
||||
qDebug() << "closed session id:" << sessionId;
|
||||
}
|
||||
}
|
||||
qDebug() << "Server::removeClient: removed" << (void *)client << ";" << clients.size() << "clients; "
|
||||
<< users.size() << "users left";
|
||||
}
|
||||
|
||||
QList<QString> Server::getOnlineModeratorList() const
|
||||
{
|
||||
// clients list should be locked by calling function prior to iteration otherwise sigfaults may occur
|
||||
QList<QString> results;
|
||||
for (auto &client : clients) {
|
||||
ServerInfo_User *data = client->getUserInfo();
|
||||
|
||||
// TODO: this line should be updated in the event there is any type of new user level created
|
||||
if (data &&
|
||||
(data->user_level() & ServerInfo_User::IsModerator || data->user_level() & ServerInfo_User::IsAdmin))
|
||||
results << QString::fromStdString(data->name()).simplified();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
void Server::externalUserJoined(const ServerInfo_User &userInfo)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
clientsLock.lockForWrite();
|
||||
|
||||
Server_RemoteUserInterface *newUser = new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo));
|
||||
externalUsers.insert(QString::fromStdString(userInfo.name()), newUser);
|
||||
externalUsersBySessionId.insert(userInfo.session_id(), newUser);
|
||||
|
||||
Event_UserJoined event;
|
||||
event.mutable_user_info()->CopyFrom(userInfo);
|
||||
|
||||
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
for (auto &client : clients)
|
||||
if (client->getAcceptsUserListChanges())
|
||||
client->sendProtocolItem(*se);
|
||||
delete se;
|
||||
clientsLock.unlock();
|
||||
|
||||
ResponseContainer rc(-1);
|
||||
newUser->joinPersistentGames(rc);
|
||||
newUser->sendResponseContainer(rc, Response::RespNothing);
|
||||
}
|
||||
|
||||
void Server::externalUserLeft(const QString &userName)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
clientsLock.lockForWrite();
|
||||
Server_AbstractUserInterface *user = externalUsers.take(userName);
|
||||
externalUsersBySessionId.remove(user->getUserInfo()->session_id());
|
||||
clientsLock.unlock();
|
||||
|
||||
QMap<int, QPair<int, int>> userGames(user->getGames());
|
||||
QMapIterator<int, QPair<int, int>> userGamesIterator(userGames);
|
||||
roomsLock.lockForRead();
|
||||
while (userGamesIterator.hasNext()) {
|
||||
userGamesIterator.next();
|
||||
Server_Room *room = rooms.value(userGamesIterator.value().first);
|
||||
if (!room)
|
||||
continue;
|
||||
|
||||
QReadLocker roomGamesLocker(&room->gamesLock);
|
||||
Server_Game *game = room->getGames().value(userGamesIterator.key());
|
||||
if (!game)
|
||||
continue;
|
||||
|
||||
QMutexLocker gameLocker(&game->gameMutex);
|
||||
auto *participant = game->getParticipants().value(userGamesIterator.value().second);
|
||||
if (!participant)
|
||||
continue;
|
||||
|
||||
participant->disconnectClient();
|
||||
}
|
||||
roomsLock.unlock();
|
||||
|
||||
delete user;
|
||||
|
||||
Event_UserLeft event;
|
||||
event.set_name(userName.toStdString());
|
||||
|
||||
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
clientsLock.lockForRead();
|
||||
for (auto &client : clients)
|
||||
if (client->getAcceptsUserListChanges())
|
||||
client->sendProtocolItem(*se);
|
||||
clientsLock.unlock();
|
||||
delete se;
|
||||
}
|
||||
|
||||
void Server::externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
QReadLocker locker(&roomsLock);
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (!room) {
|
||||
qDebug() << "externalRoomUserJoined: room id=" << roomId << "not found";
|
||||
return;
|
||||
}
|
||||
room->addExternalUser(userInfo);
|
||||
}
|
||||
|
||||
void Server::externalRoomUserLeft(int roomId, const QString &userName)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
QReadLocker locker(&roomsLock);
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (!room) {
|
||||
qDebug() << "externalRoomUserLeft: room id=" << roomId << "not found";
|
||||
return;
|
||||
}
|
||||
room->removeExternalUser(userName);
|
||||
}
|
||||
|
||||
void Server::externalRoomSay(int roomId, const QString &userName, const QString &message)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
QReadLocker locker(&roomsLock);
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (!room) {
|
||||
qDebug() << "externalRoomSay: room id=" << roomId << "not found";
|
||||
return;
|
||||
}
|
||||
room->say(userName, message, false);
|
||||
|
||||
getDatabaseInterface()->logMessage(0, userName, "ISL", message, Server_DatabaseInterface::MessageTargetIslRoom,
|
||||
room->getId(), room->getName());
|
||||
}
|
||||
|
||||
void Server::externalRoomRemoveMessages(int roomId, const QString &userName, int amount)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
QReadLocker locker(&roomsLock);
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (room == nullptr) {
|
||||
qDebug() << "externalRoomRemoveMessages: room id=" << roomId << "not found";
|
||||
return;
|
||||
}
|
||||
room->removeSaidMessages(userName, amount);
|
||||
}
|
||||
|
||||
void Server::externalRoomGameListChanged(int roomId, const ServerInfo_Game &gameInfo)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
QReadLocker locker(&roomsLock);
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (!room) {
|
||||
qDebug() << "externalRoomGameListChanged: room id=" << roomId << "not found";
|
||||
return;
|
||||
}
|
||||
room->updateExternalGameList(gameInfo);
|
||||
}
|
||||
|
||||
void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd,
|
||||
int cmdId,
|
||||
int roomId,
|
||||
int serverId,
|
||||
qint64 sessionId)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
try {
|
||||
QReadLocker roomsLocker(&roomsLock);
|
||||
QReadLocker clientsLocker(&clientsLock);
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (!room) {
|
||||
qDebug() << "externalJoinGameCommandReceived: room id=" << roomId << "not found";
|
||||
throw Response::RespNotInRoom;
|
||||
}
|
||||
Server_AbstractUserInterface *userInterface = externalUsersBySessionId.value(sessionId);
|
||||
if (!userInterface) {
|
||||
qDebug() << "externalJoinGameCommandReceived: session id=" << sessionId << "not found";
|
||||
throw Response::RespNotInRoom;
|
||||
}
|
||||
|
||||
ResponseContainer responseContainer(cmdId);
|
||||
Response::ResponseCode responseCode = room->processJoinGameCommand(cmd, responseContainer, userInterface);
|
||||
userInterface->sendResponseContainer(responseContainer, responseCode);
|
||||
} catch (Response::ResponseCode &code) {
|
||||
Response response;
|
||||
response.set_cmd_id(static_cast<google::protobuf::uint64>(cmdId));
|
||||
response.set_response_code(code);
|
||||
|
||||
sendIsl_Response(response, serverId, sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::externalGameCommandContainerReceived(const CommandContainer &cont,
|
||||
int playerId,
|
||||
int serverId,
|
||||
qint64 sessionId)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
try {
|
||||
ResponseContainer responseContainer(static_cast<int>(cont.cmd_id()));
|
||||
Response::ResponseCode finalResponseCode = Response::RespOk;
|
||||
|
||||
QReadLocker roomsLocker(&roomsLock);
|
||||
Server_Room *room = rooms.value(cont.room_id());
|
||||
if (!room) {
|
||||
qDebug() << "externalGameCommandContainerReceived: room id=" << cont.room_id() << "not found";
|
||||
throw Response::RespNotInRoom;
|
||||
}
|
||||
|
||||
QReadLocker roomGamesLocker(&room->gamesLock);
|
||||
Server_Game *game = room->getGames().value(cont.game_id());
|
||||
if (!game) {
|
||||
qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found";
|
||||
throw Response::RespNotInRoom;
|
||||
}
|
||||
|
||||
QMutexLocker gameLocker(&game->gameMutex);
|
||||
auto *participant = game->getParticipants().value(playerId);
|
||||
if (!participant) {
|
||||
qDebug() << "externalGameCommandContainerReceived: player id=" << playerId << "not found";
|
||||
throw Response::RespNotInRoom;
|
||||
}
|
||||
|
||||
GameEventStorage ges;
|
||||
for (int i = cont.game_command_size() - 1; i >= 0; --i) {
|
||||
const GameCommand &sc = cont.game_command(i);
|
||||
qDebug() << "[ISL]" << getSafeDebugString(sc);
|
||||
|
||||
Response::ResponseCode resp = participant->processGameCommand(sc, responseContainer, ges);
|
||||
|
||||
if (resp != Response::RespOk)
|
||||
finalResponseCode = resp;
|
||||
}
|
||||
ges.sendToGame(game);
|
||||
|
||||
if (finalResponseCode != Response::RespNothing) {
|
||||
participant->getUserInterface()->sendResponseContainer(responseContainer, finalResponseCode);
|
||||
}
|
||||
} catch (Response::ResponseCode code) {
|
||||
Response response;
|
||||
response.set_cmd_id(cont.cmd_id());
|
||||
response.set_response_code(code);
|
||||
|
||||
sendIsl_Response(response, serverId, sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
QReadLocker usersLocker(&clientsLock);
|
||||
|
||||
Server_ProtocolHandler *client = usersBySessionId.value(sessionId);
|
||||
if (!client) {
|
||||
qDebug() << "externalGameEventContainerReceived: session" << sessionId << "not found";
|
||||
return;
|
||||
}
|
||||
client->sendProtocolItem(cont);
|
||||
}
|
||||
|
||||
void Server::externalResponseReceived(const Response &resp, qint64 sessionId)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
QReadLocker usersLocker(&clientsLock);
|
||||
|
||||
Server_ProtocolHandler *client = usersBySessionId.value(sessionId);
|
||||
if (!client) {
|
||||
qDebug() << "externalResponseReceived: session" << sessionId << "not found";
|
||||
return;
|
||||
}
|
||||
client->sendProtocolItem(resp);
|
||||
}
|
||||
|
||||
void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
Event_ListRooms event;
|
||||
event.add_room_list()->CopyFrom(roomInfo);
|
||||
|
||||
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
|
||||
clientsLock.lockForRead();
|
||||
for (auto &client : clients)
|
||||
if (client->getAcceptsRoomListChanges())
|
||||
client->sendProtocolItem(*se);
|
||||
clientsLock.unlock();
|
||||
|
||||
if (sendToIsl)
|
||||
sendIsl_SessionEvent(*se);
|
||||
|
||||
delete se;
|
||||
}
|
||||
|
||||
void Server::addRoom(Server_Room *newRoom)
|
||||
{
|
||||
QWriteLocker locker(&roomsLock);
|
||||
qDebug() << "Adding room: ID=" << newRoom->getId() << "name=" << newRoom->getName();
|
||||
rooms.insert(newRoom->getId(), newRoom);
|
||||
connect(
|
||||
newRoom, &Server_Room::roomInfoChanged, this, [this](auto roomInfo) { broadcastRoomUpdate(roomInfo); },
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
int Server::getUsersCount() const
|
||||
{
|
||||
QReadLocker locker(&clientsLock);
|
||||
return users.size();
|
||||
}
|
||||
|
||||
int Server::getGamesCount() const
|
||||
{
|
||||
int result = 0;
|
||||
QReadLocker locker(&roomsLock);
|
||||
QMapIterator<int, Server_Room *> roomIterator(rooms);
|
||||
while (roomIterator.hasNext()) {
|
||||
Server_Room *room = roomIterator.next().value();
|
||||
QReadLocker roomLocker(&room->gamesLock);
|
||||
result += room->getGames().size();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Server::sendIsl_Response(const Response &item, int serverId, qint64 sessionId)
|
||||
{
|
||||
IslMessage msg;
|
||||
msg.set_message_type(IslMessage::RESPONSE);
|
||||
if (sessionId != -1)
|
||||
msg.set_session_id(static_cast<google::protobuf::uint64>(sessionId));
|
||||
msg.mutable_response()->CopyFrom(item);
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
||||
void Server::sendIsl_SessionEvent(const SessionEvent &item, int serverId, qint64 sessionId)
|
||||
{
|
||||
IslMessage msg;
|
||||
msg.set_message_type(IslMessage::SESSION_EVENT);
|
||||
if (sessionId != -1)
|
||||
msg.set_session_id(static_cast<google::protobuf::uint64>(sessionId));
|
||||
msg.mutable_session_event()->CopyFrom(item);
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
||||
void Server::sendIsl_GameEventContainer(const GameEventContainer &item, int serverId, qint64 sessionId)
|
||||
{
|
||||
IslMessage msg;
|
||||
msg.set_message_type(IslMessage::GAME_EVENT_CONTAINER);
|
||||
if (sessionId != -1)
|
||||
msg.set_session_id(static_cast<google::protobuf::uint64>(sessionId));
|
||||
msg.mutable_game_event_container()->CopyFrom(item);
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
||||
void Server::sendIsl_RoomEvent(const RoomEvent &item, int serverId, qint64 sessionId)
|
||||
{
|
||||
IslMessage msg;
|
||||
msg.set_message_type(IslMessage::ROOM_EVENT);
|
||||
if (sessionId != -1)
|
||||
msg.set_session_id(static_cast<google::protobuf::uint64>(sessionId));
|
||||
msg.mutable_room_event()->CopyFrom(item);
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
||||
void Server::sendIsl_GameCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId, int playerId)
|
||||
{
|
||||
IslMessage msg;
|
||||
msg.set_message_type(IslMessage::GAME_COMMAND_CONTAINER);
|
||||
msg.set_session_id(static_cast<google::protobuf::uint64>(sessionId));
|
||||
msg.set_player_id(playerId);
|
||||
|
||||
CommandContainer *cont = msg.mutable_game_command();
|
||||
cont->CopyFrom(item);
|
||||
cont->set_room_id(static_cast<google::protobuf::uint32>(roomId));
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
||||
void Server::sendIsl_RoomCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId)
|
||||
{
|
||||
IslMessage msg;
|
||||
msg.set_message_type(IslMessage::ROOM_COMMAND_CONTAINER);
|
||||
msg.set_session_id(static_cast<google::protobuf::uint64>(sessionId));
|
||||
|
||||
CommandContainer *cont = msg.mutable_room_command();
|
||||
cont->CopyFrom(item);
|
||||
cont->set_room_id(static_cast<google::protobuf::uint32>(roomId));
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
253
common/server/server.h
Normal file
253
common/server/server.h
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include "pb/commands.pb.h"
|
||||
#include "pb/serverinfo_ban.pb.h"
|
||||
#include "pb/serverinfo_chat_message.pb.h"
|
||||
#include "pb/serverinfo_user.pb.h"
|
||||
#include "pb/serverinfo_warning.pb.h"
|
||||
#include "server_player_reference.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QMultiMap>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QReadWriteLock>
|
||||
#include <QStringList>
|
||||
|
||||
class Server_DatabaseInterface;
|
||||
class Server_Game;
|
||||
class Server_Room;
|
||||
class Server_ProtocolHandler;
|
||||
class Server_AbstractUserInterface;
|
||||
class GameReplay;
|
||||
class IslMessage;
|
||||
class SessionEvent;
|
||||
class RoomEvent;
|
||||
class DeckList;
|
||||
class ServerInfo_Game;
|
||||
class ServerInfo_Room;
|
||||
class Response;
|
||||
class GameEventContainer;
|
||||
class CommandContainer;
|
||||
class Command_JoinGame;
|
||||
|
||||
enum AuthenticationResult
|
||||
{
|
||||
NotLoggedIn,
|
||||
PasswordRight,
|
||||
UnknownUser,
|
||||
WouldOverwriteOldSession,
|
||||
UserIsBanned,
|
||||
UsernameInvalid,
|
||||
RegistrationRequired,
|
||||
UserIsInactive,
|
||||
ClientIdRequired
|
||||
};
|
||||
|
||||
class Server : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void pingClockTimeout();
|
||||
void sigSendIslMessage(const IslMessage &message, int serverId);
|
||||
void endSession(qint64 sessionId);
|
||||
private slots:
|
||||
void broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl = false);
|
||||
|
||||
public:
|
||||
mutable QReadWriteLock clientsLock, roomsLock; // locking order: roomsLock before clientsLock
|
||||
explicit Server(QObject *parent = nullptr);
|
||||
~Server() override = default;
|
||||
AuthenticationResult loginUser(Server_ProtocolHandler *session,
|
||||
QString &name,
|
||||
const QString &password,
|
||||
bool passwordNeedsHash,
|
||||
QString &reason,
|
||||
int &secondsLeft,
|
||||
QString &clientid,
|
||||
QString &clientVersion,
|
||||
QString &connectionType);
|
||||
|
||||
const QMap<int, Server_Room *> &getRooms()
|
||||
{
|
||||
return rooms;
|
||||
}
|
||||
|
||||
Server_AbstractUserInterface *findUser(const QString &userName) const;
|
||||
const QMap<QString, Server_ProtocolHandler *> &getUsers() const
|
||||
{
|
||||
return users;
|
||||
}
|
||||
const QMap<qint64, Server_ProtocolHandler *> &getUsersBySessionId() const
|
||||
{
|
||||
return usersBySessionId;
|
||||
}
|
||||
virtual QMap<QString, bool> getServerRequiredFeatureList() const
|
||||
{
|
||||
return QMap<QString, bool>();
|
||||
}
|
||||
void addClient(Server_ProtocolHandler *player);
|
||||
void removeClient(Server_ProtocolHandler *player);
|
||||
QList<QString> getOnlineModeratorList() const;
|
||||
virtual QString getLoginMessage() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
virtual QString getRequiredFeatures() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
virtual bool permitUnregisteredUsers() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual bool getGameShouldPing() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool getClientIDRequiredEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool getRegOnlyServerEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool getMaxUserLimitEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool getEnableLogQuery() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool getStoreReplaysEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual int getIdleClientTimeout() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int getClientKeepAlive() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int getMaxGameInactivityTime() const
|
||||
{
|
||||
return 9999999;
|
||||
}
|
||||
virtual int getMaxPlayerInactivityTime() const
|
||||
{
|
||||
return 9999999;
|
||||
}
|
||||
virtual int getMessageCountingInterval() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int getMaxMessageCountPerInterval() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int getMaxMessageSizePerInterval() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int getMaxGamesPerUser() const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
virtual int getCommandCountingInterval() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int getMaxCommandCountPerInterval() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int getMaxUserTotal() const
|
||||
{
|
||||
return 9999999;
|
||||
}
|
||||
virtual int getServerID() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual bool permitCreateGameAsJudge() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Server_DatabaseInterface *getDatabaseInterface() const;
|
||||
int getNextLocalGameId()
|
||||
{
|
||||
QMutexLocker locker(&nextLocalGameIdMutex);
|
||||
return ++nextLocalGameId;
|
||||
}
|
||||
|
||||
void sendIsl_Response(const Response &item, int serverId = -1, qint64 sessionId = -1);
|
||||
void sendIsl_SessionEvent(const SessionEvent &item, int serverId = -1, qint64 sessionId = -1);
|
||||
void sendIsl_GameEventContainer(const GameEventContainer &item, int serverId = -1, qint64 sessionId = -1);
|
||||
void sendIsl_RoomEvent(const RoomEvent &item, int serverId = -1, qint64 sessionId = -1);
|
||||
void sendIsl_GameCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId, int playerId);
|
||||
void sendIsl_RoomCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId);
|
||||
|
||||
const QMap<QString, Server_AbstractUserInterface *> &getExternalUsers() const
|
||||
{
|
||||
return externalUsers;
|
||||
}
|
||||
|
||||
void addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId);
|
||||
void removePersistentPlayer(const QString &userName, int roomId, int gameId, int playerId);
|
||||
QList<PlayerReference> getPersistentPlayerReferences(const QString &userName) const;
|
||||
int getUsersCount() const;
|
||||
int getGamesCount() const;
|
||||
int getTCPUserCount() const
|
||||
{
|
||||
return tcpUserCount;
|
||||
}
|
||||
int getWebSocketUserCount() const
|
||||
{
|
||||
return webSocketUserCount;
|
||||
}
|
||||
|
||||
private:
|
||||
QMultiMap<QString, PlayerReference> persistentPlayers;
|
||||
mutable QReadWriteLock persistentPlayersLock;
|
||||
int nextLocalGameId, tcpUserCount, webSocketUserCount;
|
||||
QMutex nextLocalGameIdMutex;
|
||||
|
||||
protected slots:
|
||||
void externalUserJoined(const ServerInfo_User &userInfo);
|
||||
void externalUserLeft(const QString &userName);
|
||||
void externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo);
|
||||
void externalRoomUserLeft(int roomId, const QString &userName);
|
||||
void externalRoomSay(int roomId, const QString &userName, const QString &message);
|
||||
void externalRoomRemoveMessages(int roomId, const QString &userName, int amount);
|
||||
void externalRoomGameListChanged(int roomId, const ServerInfo_Game &gameInfo);
|
||||
void
|
||||
externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cmdId, int roomId, int serverId, qint64 sessionId);
|
||||
void
|
||||
externalGameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId);
|
||||
void externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId);
|
||||
void externalResponseReceived(const Response &resp, qint64 sessionId);
|
||||
|
||||
virtual void doSendIslMessage(const IslMessage & /* msg */, int /* serverId */)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void prepareDestroy();
|
||||
void setDatabaseInterface(Server_DatabaseInterface *_databaseInterface);
|
||||
QList<Server_ProtocolHandler *> clients;
|
||||
QMap<qint64, Server_ProtocolHandler *> usersBySessionId;
|
||||
QMap<QString, Server_ProtocolHandler *> users;
|
||||
QMap<qint64, Server_AbstractUserInterface *> externalUsersBySessionId;
|
||||
QMap<QString, Server_AbstractUserInterface *> externalUsers;
|
||||
QMap<int, Server_Room *> rooms;
|
||||
QMap<QThread *, Server_DatabaseInterface *> databaseInterfaces;
|
||||
void addRoom(Server_Room *newRoom);
|
||||
};
|
||||
|
||||
#endif
|
||||
116
common/server/server_abstractuserinterface.cpp
Normal file
116
common/server/server_abstractuserinterface.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#include "server_abstractuserinterface.h"
|
||||
|
||||
#include "game/server_game.h"
|
||||
#include "game/server_player.h"
|
||||
#include "pb/event_game_joined.pb.h"
|
||||
#include "pb/event_game_state_changed.pb.h"
|
||||
#include "server.h"
|
||||
#include "server_player_reference.h"
|
||||
#include "server_response_containers.h"
|
||||
#include "server_room.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
void Server_AbstractUserInterface::sendProtocolItemByType(ServerMessage::MessageType type,
|
||||
const ::google::protobuf::Message &item)
|
||||
{
|
||||
switch (type) {
|
||||
case ServerMessage::RESPONSE:
|
||||
sendProtocolItem(static_cast<const Response &>(item));
|
||||
break;
|
||||
case ServerMessage::SESSION_EVENT:
|
||||
sendProtocolItem(static_cast<const SessionEvent &>(item));
|
||||
break;
|
||||
case ServerMessage::GAME_EVENT_CONTAINER:
|
||||
sendProtocolItem(static_cast<const GameEventContainer &>(item));
|
||||
break;
|
||||
case ServerMessage::ROOM_EVENT:
|
||||
sendProtocolItem(static_cast<const RoomEvent &>(item));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SessionEvent *Server_AbstractUserInterface::prepareSessionEvent(const ::google::protobuf::Message &sessionEvent)
|
||||
{
|
||||
SessionEvent *event = new SessionEvent;
|
||||
event->GetReflection()
|
||||
->MutableMessage(event, sessionEvent.GetDescriptor()->FindExtensionByName("ext"))
|
||||
->CopyFrom(sessionEvent);
|
||||
return event;
|
||||
}
|
||||
|
||||
void Server_AbstractUserInterface::sendResponseContainer(const ResponseContainer &responseContainer,
|
||||
Response::ResponseCode responseCode)
|
||||
{
|
||||
const QList<QPair<ServerMessage::MessageType, ::google::protobuf::Message *>> &preResponseQueue =
|
||||
responseContainer.getPreResponseQueue();
|
||||
for (int i = 0; i < preResponseQueue.size(); ++i)
|
||||
sendProtocolItemByType(preResponseQueue[i].first, *preResponseQueue[i].second);
|
||||
|
||||
if (responseCode != Response::RespNothing) {
|
||||
Response response;
|
||||
response.set_cmd_id(responseContainer.getCmdId());
|
||||
response.set_response_code(responseCode);
|
||||
::google::protobuf::Message *responseExtension = responseContainer.getResponseExtension();
|
||||
if (responseExtension)
|
||||
response.GetReflection()
|
||||
->MutableMessage(&response, responseExtension->GetDescriptor()->FindExtensionByName("ext"))
|
||||
->CopyFrom(*responseExtension);
|
||||
sendProtocolItem(response);
|
||||
}
|
||||
|
||||
const QList<QPair<ServerMessage::MessageType, ::google::protobuf::Message *>> &postResponseQueue =
|
||||
responseContainer.getPostResponseQueue();
|
||||
for (int i = 0; i < postResponseQueue.size(); ++i)
|
||||
sendProtocolItemByType(postResponseQueue[i].first, *postResponseQueue[i].second);
|
||||
}
|
||||
|
||||
void Server_AbstractUserInterface::playerRemovedFromGame(Server_Game *game)
|
||||
{
|
||||
qDebug() << "Server_AbstractUserInterface::playerRemovedFromGame(): gameId =" << game->getGameId();
|
||||
|
||||
QMutexLocker locker(&gameListMutex);
|
||||
games.remove(game->getGameId());
|
||||
}
|
||||
|
||||
void Server_AbstractUserInterface::playerAddedToGame(int gameId, int roomId, int playerId)
|
||||
{
|
||||
qDebug() << "Server_AbstractUserInterface::playerAddedToGame(): gameId =" << gameId;
|
||||
|
||||
QMutexLocker locker(&gameListMutex);
|
||||
games.insert(gameId, QPair<int, int>(roomId, playerId));
|
||||
}
|
||||
|
||||
void Server_AbstractUserInterface::joinPersistentGames(ResponseContainer &rc)
|
||||
{
|
||||
QList<PlayerReference> gamesToJoin =
|
||||
server->getPersistentPlayerReferences(QString::fromStdString(userInfo->name()));
|
||||
|
||||
server->roomsLock.lockForRead();
|
||||
for (int i = 0; i < gamesToJoin.size(); ++i) {
|
||||
const PlayerReference &pr = gamesToJoin.at(i);
|
||||
|
||||
Server_Room *room = server->getRooms().value(pr.getRoomId());
|
||||
if (!room)
|
||||
continue;
|
||||
QReadLocker roomGamesLocker(&room->gamesLock);
|
||||
|
||||
Server_Game *game = room->getGames().value(pr.getGameId());
|
||||
if (!game)
|
||||
continue;
|
||||
QMutexLocker gameLocker(&game->gameMutex);
|
||||
|
||||
auto *participant = game->getParticipants().value(pr.getPlayerId());
|
||||
if (!participant)
|
||||
continue;
|
||||
|
||||
participant->setUserInterface(this);
|
||||
playerAddedToGame(game->getGameId(), room->getId(), participant->getPlayerId());
|
||||
|
||||
game->createGameJoinedEvent(participant, rc, true);
|
||||
}
|
||||
server->roomsLock.unlock();
|
||||
}
|
||||
63
common/server/server_abstractuserinterface.h
Normal file
63
common/server/server_abstractuserinterface.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef SERVER_ABSTRACTUSERINTERFACE
|
||||
#define SERVER_ABSTRACTUSERINTERFACE
|
||||
|
||||
#include "../serverinfo_user_container.h"
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/server_message.pb.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QPair>
|
||||
|
||||
class SessionEvent;
|
||||
class GameEventContainer;
|
||||
class RoomEvent;
|
||||
class ResponseContainer;
|
||||
|
||||
class Server;
|
||||
class Server_Game;
|
||||
|
||||
class Server_AbstractUserInterface : public ServerInfo_User_Container
|
||||
{
|
||||
private:
|
||||
mutable QMutex gameListMutex;
|
||||
QMap<int, QPair<int, int>> games; // gameId -> (roomId, playerId)
|
||||
protected:
|
||||
Server *server;
|
||||
|
||||
public:
|
||||
explicit Server_AbstractUserInterface(Server *_server) : server(_server)
|
||||
{
|
||||
}
|
||||
Server_AbstractUserInterface(Server *_server, const ServerInfo_User_Container &other)
|
||||
: ServerInfo_User_Container(other), server(_server)
|
||||
{
|
||||
}
|
||||
~Server_AbstractUserInterface() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual int getLastCommandTime() const = 0;
|
||||
virtual bool addSaidMessageSize(int size) = 0;
|
||||
|
||||
void playerRemovedFromGame(Server_Game *game);
|
||||
void playerAddedToGame(int gameId, int roomId, int playerId);
|
||||
void joinPersistentGames(ResponseContainer &rc);
|
||||
|
||||
QMap<int, QPair<int, int>> getGames() const
|
||||
{
|
||||
QMutexLocker locker(&gameListMutex);
|
||||
return games;
|
||||
}
|
||||
|
||||
virtual void sendProtocolItem(const Response &item) = 0;
|
||||
virtual void sendProtocolItem(const SessionEvent &item) = 0;
|
||||
virtual void sendProtocolItem(const GameEventContainer &item) = 0;
|
||||
virtual void sendProtocolItem(const RoomEvent &item) = 0;
|
||||
void sendProtocolItemByType(ServerMessage::MessageType type, const ::google::protobuf::Message &item);
|
||||
|
||||
static SessionEvent *prepareSessionEvent(const ::google::protobuf::Message &sessionEvent);
|
||||
void sendResponseContainer(const ResponseContainer &responseContainer, Response::ResponseCode responseCode);
|
||||
};
|
||||
|
||||
#endif
|
||||
2
common/server/server_database_interface.cpp
Normal file
2
common/server/server_database_interface.cpp
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "server_database_interface.h"
|
||||
175
common/server/server_database_interface.h
Normal file
175
common/server/server_database_interface.h
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
#ifndef SERVER_DATABASE_INTERFACE_H
|
||||
#define SERVER_DATABASE_INTERFACE_H
|
||||
|
||||
#include "server.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class Server_DatabaseInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Server_DatabaseInterface(QObject *parent = nullptr) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler,
|
||||
const QString &user,
|
||||
const QString &password,
|
||||
const QString &clientId,
|
||||
QString &reasonStr,
|
||||
int &secondsLeft,
|
||||
bool passwordNeedsHash) = 0;
|
||||
virtual bool checkUserIsBanned(const QString & /* ipAddress */,
|
||||
const QString & /* userName */,
|
||||
const QString & /* clientId */,
|
||||
QString & /* banReason */,
|
||||
int & /* banSecondsRemaining */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool activeUserExists(const QString & /* user */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool userExists(const QString & /* user */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual QString getUserSalt(const QString & /* user */)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
virtual QMap<QString, ServerInfo_User> getBuddyList(const QString & /* name */)
|
||||
{
|
||||
return QMap<QString, ServerInfo_User>();
|
||||
}
|
||||
virtual QMap<QString, ServerInfo_User> getIgnoreList(const QString & /* name */)
|
||||
{
|
||||
return QMap<QString, ServerInfo_User>();
|
||||
}
|
||||
virtual bool isInBuddyList(const QString & /* whoseList */, const QString & /* who */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool isInIgnoreList(const QString & /* whoseList */, const QString & /* who */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0;
|
||||
virtual void storeGameInformation(const QString & /* roomName */,
|
||||
const QStringList & /* roomGameTypes */,
|
||||
const ServerInfo_Game & /* gameInfo */,
|
||||
const QSet<QString> & /* allPlayersEver */,
|
||||
const QSet<QString> & /* allSpectatorsEver */,
|
||||
const QList<GameReplay *> & /* replayList */)
|
||||
{
|
||||
}
|
||||
virtual DeckList *getDeckFromDatabase(int /* deckId */, int /* userId */)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual bool removeForgotPassword(const QString & /* user */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual qint64 startSession(const QString & /* userName */,
|
||||
const QString & /* address */,
|
||||
const QString & /* clientId */,
|
||||
const QString & /* connectionType */)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual bool usernameIsValid(const QString & /*userName */, QString & /* error */)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
public slots:
|
||||
virtual void endSession(qint64 /* sessionId */)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual int getNextGameId() = 0;
|
||||
virtual int getNextReplayId() = 0;
|
||||
virtual int getActiveUserCount(QString connectionType = QString()) = 0;
|
||||
|
||||
virtual void clearSessionTables()
|
||||
{
|
||||
}
|
||||
virtual void lockSessionTables()
|
||||
{
|
||||
}
|
||||
virtual void unlockSessionTables()
|
||||
{
|
||||
}
|
||||
virtual bool userSessionExists(const QString & /* userName */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool getRequireRegistration()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool registerUser(const QString & /* userName */,
|
||||
const QString & /* realName */,
|
||||
const QString & /* password */,
|
||||
bool /* passwordNeedsHash */,
|
||||
const QString & /* emailAddress */,
|
||||
const QString & /* country */,
|
||||
bool /* active = false */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool activateUser(const QString & /* userName */, const QString & /* token */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual void updateUsersClientID(const QString & /* userName */, const QString & /* userClientID */)
|
||||
{
|
||||
}
|
||||
virtual void updateUsersLastLoginData(const QString & /* userName */, const QString & /* clientVersion */)
|
||||
{
|
||||
}
|
||||
|
||||
enum LogMessage_TargetType
|
||||
{
|
||||
MessageTargetRoom,
|
||||
MessageTargetGame,
|
||||
MessageTargetChat,
|
||||
MessageTargetIslRoom
|
||||
};
|
||||
virtual void logMessage(const int /* senderId */,
|
||||
const QString & /* senderName */,
|
||||
const QString & /* senderIp */,
|
||||
const QString & /* logMessage */,
|
||||
LogMessage_TargetType /* targetType */,
|
||||
const int /* targetId */,
|
||||
const QString & /* targetName */){};
|
||||
virtual bool checkUserIsBanned(Server_ProtocolHandler * /* session */,
|
||||
QString & /* banReason */,
|
||||
int & /* banSecondsRemaining */)
|
||||
{
|
||||
return false;
|
||||
};
|
||||
virtual int checkNumberOfUserAccounts(const QString & /* email */)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
virtual bool
|
||||
changeUserPassword(const QString & /* user */, const QString & /* password */, bool /* passwordNeedsHash */)
|
||||
{
|
||||
return false;
|
||||
};
|
||||
virtual bool changeUserPassword(const QString & /* user */,
|
||||
const QString & /* oldPassword */,
|
||||
bool /* oldPasswordNeedsHash */,
|
||||
const QString & /* newPassword */,
|
||||
bool /* newPasswordNeedsHash */)
|
||||
{
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
33
common/server/server_player_reference.h
Normal file
33
common/server/server_player_reference.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef SERVER_PLAYER_REFERENCE_H
|
||||
#define SERVER_PLAYER_REFERENCE_H
|
||||
|
||||
class PlayerReference
|
||||
{
|
||||
private:
|
||||
int roomId;
|
||||
int gameId;
|
||||
int playerId;
|
||||
|
||||
public:
|
||||
PlayerReference(int _roomId, int _gameId, int _playerId) : roomId(_roomId), gameId(_gameId), playerId(_playerId)
|
||||
{
|
||||
}
|
||||
int getRoomId() const
|
||||
{
|
||||
return roomId;
|
||||
}
|
||||
int getGameId() const
|
||||
{
|
||||
return gameId;
|
||||
}
|
||||
int getPlayerId() const
|
||||
{
|
||||
return playerId;
|
||||
}
|
||||
bool operator==(const PlayerReference &other)
|
||||
{
|
||||
return ((roomId == other.roomId) && (gameId == other.gameId) && (playerId == other.playerId));
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
865
common/server/server_protocolhandler.cpp
Normal file
865
common/server/server_protocolhandler.cpp
Normal file
|
|
@ -0,0 +1,865 @@
|
|||
#include "server_protocolhandler.h"
|
||||
|
||||
#include "../debug_pb_message.h"
|
||||
#include "../featureset.h"
|
||||
#include "../get_pb_extension.h"
|
||||
#include "../trice_limits.h"
|
||||
#include "game/server_game.h"
|
||||
#include "game/server_player.h"
|
||||
#include "pb/commands.pb.h"
|
||||
#include "pb/event_game_joined.pb.h"
|
||||
#include "pb/event_list_rooms.pb.h"
|
||||
#include "pb/event_notify_user.pb.h"
|
||||
#include "pb/event_room_say.pb.h"
|
||||
#include "pb/event_server_message.pb.h"
|
||||
#include "pb/event_user_message.pb.h"
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/response_get_games_of_user.pb.h"
|
||||
#include "pb/response_get_user_info.pb.h"
|
||||
#include "pb/response_join_room.pb.h"
|
||||
#include "pb/response_list_users.pb.h"
|
||||
#include "pb/response_login.pb.h"
|
||||
#include "pb/serverinfo_user.pb.h"
|
||||
#include "server_database_interface.h"
|
||||
#include "server_room.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QtMath>
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
Server_ProtocolHandler::Server_ProtocolHandler(Server *_server,
|
||||
Server_DatabaseInterface *_databaseInterface,
|
||||
QObject *parent)
|
||||
: QObject(parent), Server_AbstractUserInterface(_server), deleted(false), databaseInterface(_databaseInterface),
|
||||
authState(NotLoggedIn), usingRealPassword(false), acceptsUserListChanges(false), acceptsRoomListChanges(false),
|
||||
idleClientWarningSent(false), timeRunning(0), lastDataReceived(0), lastActionReceived(0)
|
||||
|
||||
{
|
||||
connect(server, &Server::pingClockTimeout, this, &Server_ProtocolHandler::pingClockTimeout);
|
||||
}
|
||||
|
||||
Server_ProtocolHandler::~Server_ProtocolHandler()
|
||||
{
|
||||
}
|
||||
|
||||
// This function must only be called from the thread this object lives in.
|
||||
// Except when the server is shutting down.
|
||||
// The thread must not hold any server locks when calling this (e.g. clientsLock, roomsLock).
|
||||
void Server_ProtocolHandler::prepareDestroy()
|
||||
{
|
||||
if (deleted)
|
||||
return;
|
||||
deleted = true;
|
||||
|
||||
for (auto *room : rooms.values()) {
|
||||
room->removeClient(this);
|
||||
}
|
||||
|
||||
QMap<int, QPair<int, int>> tempGames(getGames());
|
||||
|
||||
server->roomsLock.lockForRead();
|
||||
QMapIterator<int, QPair<int, int>> gameIterator(tempGames);
|
||||
while (gameIterator.hasNext()) {
|
||||
gameIterator.next();
|
||||
|
||||
Server_Room *room = server->getRooms().value(gameIterator.value().first);
|
||||
if (!room)
|
||||
continue;
|
||||
room->gamesLock.lockForRead();
|
||||
Server_Game *game = room->getGames().value(gameIterator.key());
|
||||
if (!game) {
|
||||
room->gamesLock.unlock();
|
||||
continue;
|
||||
}
|
||||
game->gameMutex.lock();
|
||||
auto *participant = game->getParticipants().value(gameIterator.value().second);
|
||||
if (!participant) {
|
||||
game->gameMutex.unlock();
|
||||
room->gamesLock.unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
participant->disconnectClient();
|
||||
|
||||
game->gameMutex.unlock();
|
||||
room->gamesLock.unlock();
|
||||
}
|
||||
server->roomsLock.unlock();
|
||||
|
||||
server->removeClient(this);
|
||||
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void Server_ProtocolHandler::sendProtocolItem(const Response &item)
|
||||
{
|
||||
ServerMessage msg;
|
||||
msg.mutable_response()->CopyFrom(item);
|
||||
msg.set_message_type(ServerMessage::RESPONSE);
|
||||
|
||||
transmitProtocolItem(msg);
|
||||
}
|
||||
|
||||
void Server_ProtocolHandler::sendProtocolItem(const SessionEvent &item)
|
||||
{
|
||||
ServerMessage msg;
|
||||
msg.mutable_session_event()->CopyFrom(item);
|
||||
msg.set_message_type(ServerMessage::SESSION_EVENT);
|
||||
|
||||
transmitProtocolItem(msg);
|
||||
}
|
||||
|
||||
void Server_ProtocolHandler::sendProtocolItem(const GameEventContainer &item)
|
||||
{
|
||||
ServerMessage msg;
|
||||
msg.mutable_game_event_container()->CopyFrom(item);
|
||||
msg.set_message_type(ServerMessage::GAME_EVENT_CONTAINER);
|
||||
|
||||
transmitProtocolItem(msg);
|
||||
}
|
||||
|
||||
void Server_ProtocolHandler::sendProtocolItem(const RoomEvent &item)
|
||||
{
|
||||
ServerMessage msg;
|
||||
msg.mutable_room_event()->CopyFrom(item);
|
||||
msg.set_message_type(ServerMessage::ROOM_EVENT);
|
||||
|
||||
transmitProtocolItem(msg);
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::processSessionCommandContainer(const CommandContainer &cont,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
Response::ResponseCode finalResponseCode = Response::RespOk;
|
||||
for (int i = cont.session_command_size() - 1; i >= 0; --i) {
|
||||
Response::ResponseCode resp = Response::RespInvalidCommand;
|
||||
const SessionCommand &sc = cont.session_command(i);
|
||||
const int num = getPbExtension(sc);
|
||||
if (num != SessionCommand::PING) { // don't log ping commands
|
||||
logDebugMessage(getSafeDebugString(sc));
|
||||
}
|
||||
switch ((SessionCommand::SessionCommandType)num) {
|
||||
case SessionCommand::PING:
|
||||
resp = cmdPing(sc.GetExtension(Command_Ping::ext), rc);
|
||||
break;
|
||||
case SessionCommand::LOGIN:
|
||||
resp = cmdLogin(sc.GetExtension(Command_Login::ext), rc);
|
||||
break;
|
||||
case SessionCommand::MESSAGE:
|
||||
resp = cmdMessage(sc.GetExtension(Command_Message::ext), rc);
|
||||
break;
|
||||
case SessionCommand::GET_GAMES_OF_USER:
|
||||
resp = cmdGetGamesOfUser(sc.GetExtension(Command_GetGamesOfUser::ext), rc);
|
||||
break;
|
||||
case SessionCommand::GET_USER_INFO:
|
||||
resp = cmdGetUserInfo(sc.GetExtension(Command_GetUserInfo::ext), rc);
|
||||
break;
|
||||
case SessionCommand::LIST_ROOMS:
|
||||
resp = cmdListRooms(sc.GetExtension(Command_ListRooms::ext), rc);
|
||||
break;
|
||||
case SessionCommand::JOIN_ROOM:
|
||||
resp = cmdJoinRoom(sc.GetExtension(Command_JoinRoom::ext), rc);
|
||||
break;
|
||||
case SessionCommand::LIST_USERS:
|
||||
resp = cmdListUsers(sc.GetExtension(Command_ListUsers::ext), rc);
|
||||
break;
|
||||
default:
|
||||
resp = processExtendedSessionCommand(num, sc, rc);
|
||||
}
|
||||
if (resp != Response::RespOk)
|
||||
finalResponseCode = resp;
|
||||
}
|
||||
return finalResponseCode;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::processRoomCommandContainer(const CommandContainer &cont,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
QReadLocker locker(&server->roomsLock);
|
||||
Server_Room *room = rooms.value(cont.room_id(), 0);
|
||||
if (!room)
|
||||
return Response::RespNotInRoom;
|
||||
|
||||
resetIdleTimer();
|
||||
|
||||
Response::ResponseCode finalResponseCode = Response::RespOk;
|
||||
for (int i = cont.room_command_size() - 1; i >= 0; --i) {
|
||||
Response::ResponseCode resp = Response::RespInvalidCommand;
|
||||
const RoomCommand &sc = cont.room_command(i);
|
||||
const int num = getPbExtension(sc);
|
||||
logDebugMessage(getSafeDebugString(sc));
|
||||
switch ((RoomCommand::RoomCommandType)num) {
|
||||
case RoomCommand::LEAVE_ROOM:
|
||||
resp = cmdLeaveRoom(sc.GetExtension(Command_LeaveRoom::ext), room, rc);
|
||||
break;
|
||||
case RoomCommand::ROOM_SAY:
|
||||
resp = cmdRoomSay(sc.GetExtension(Command_RoomSay::ext), room, rc);
|
||||
break;
|
||||
case RoomCommand::CREATE_GAME:
|
||||
resp = cmdCreateGame(sc.GetExtension(Command_CreateGame::ext), room, rc);
|
||||
break;
|
||||
case RoomCommand::JOIN_GAME:
|
||||
resp = cmdJoinGame(sc.GetExtension(Command_JoinGame::ext), room, rc);
|
||||
break;
|
||||
}
|
||||
if (resp != Response::RespOk)
|
||||
finalResponseCode = resp;
|
||||
}
|
||||
return finalResponseCode;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const CommandContainer &cont,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
static QList<GameCommand::GameCommandType> antifloodCommandsWhiteList =
|
||||
QList<GameCommand::GameCommandType>()
|
||||
// draw/undo card draw (example: drawing 10 cards one by one from the deck)
|
||||
<< GameCommand::DRAW_CARDS
|
||||
<< GameCommand::UNDO_DRAW
|
||||
// create, delete arrows (example: targeting with 10 cards during an attack)
|
||||
<< GameCommand::CREATE_ARROW
|
||||
<< GameCommand::DELETE_ARROW
|
||||
// set card attributes (example: tapping 10 cards at once)
|
||||
<< GameCommand::SET_CARD_ATTR
|
||||
// increment / decrement counter (example: -10 life points one by one)
|
||||
<< GameCommand::INC_COUNTER
|
||||
// mulling lots of hands in a row
|
||||
<< GameCommand::MULLIGAN
|
||||
// allows a user to sideboard without receiving flooding message
|
||||
<< GameCommand::MOVE_CARD;
|
||||
|
||||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
QMap<int, QPair<int, int>> gameMap = getGames();
|
||||
if (!gameMap.contains(cont.game_id()))
|
||||
return Response::RespNotInRoom;
|
||||
const QPair<int, int> roomIdAndPlayerId = gameMap.value(cont.game_id());
|
||||
|
||||
QReadLocker roomsLocker(&server->roomsLock);
|
||||
Server_Room *room = server->getRooms().value(roomIdAndPlayerId.first);
|
||||
if (!room)
|
||||
return Response::RespNotInRoom;
|
||||
|
||||
QReadLocker roomGamesLocker(&room->gamesLock);
|
||||
Server_Game *game = room->getGames().value(cont.game_id());
|
||||
if (!game) {
|
||||
if (room->getExternalGames().contains(cont.game_id())) {
|
||||
server->sendIsl_GameCommand(cont, room->getExternalGames().value(cont.game_id()).server_id(),
|
||||
userInfo->session_id(), roomIdAndPlayerId.first, roomIdAndPlayerId.second);
|
||||
return Response::RespNothing;
|
||||
}
|
||||
return Response::RespNotInRoom;
|
||||
}
|
||||
|
||||
QMutexLocker gameLocker(&game->gameMutex);
|
||||
auto *participant = game->getParticipants().value(roomIdAndPlayerId.second);
|
||||
if (!participant)
|
||||
return Response::RespNotInRoom;
|
||||
|
||||
resetIdleTimer();
|
||||
|
||||
int commandCountingInterval = server->getCommandCountingInterval();
|
||||
int maxCommandCountPerInterval = server->getMaxCommandCountPerInterval();
|
||||
GameEventStorage ges;
|
||||
Response::ResponseCode finalResponseCode = Response::RespOk;
|
||||
for (int i = cont.game_command_size() - 1; i >= 0; --i) {
|
||||
const GameCommand &sc = cont.game_command(i);
|
||||
logDebugMessage(QString("game %1 player %2: ").arg(cont.game_id()).arg(roomIdAndPlayerId.second) +
|
||||
getSafeDebugString(sc));
|
||||
|
||||
if (commandCountingInterval > 0) {
|
||||
int totalCount = 0;
|
||||
if (commandCountOverTime.isEmpty())
|
||||
commandCountOverTime.prepend(0);
|
||||
|
||||
if (!antifloodCommandsWhiteList.contains((GameCommand::GameCommandType)getPbExtension(sc)))
|
||||
++commandCountOverTime[0];
|
||||
|
||||
for (int count : commandCountOverTime) {
|
||||
totalCount += count;
|
||||
}
|
||||
|
||||
if (maxCommandCountPerInterval > 0 && totalCount > maxCommandCountPerInterval) {
|
||||
return Response::RespChatFlood;
|
||||
}
|
||||
}
|
||||
|
||||
Response::ResponseCode resp = participant->processGameCommand(sc, rc, ges);
|
||||
|
||||
if (resp != Response::RespOk)
|
||||
finalResponseCode = resp;
|
||||
}
|
||||
ges.sendToGame(game);
|
||||
|
||||
return finalResponseCode;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::processModeratorCommandContainer(const CommandContainer &cont,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
if (!userInfo)
|
||||
return Response::RespLoginNeeded;
|
||||
if (!(userInfo->user_level() & ServerInfo_User::IsModerator))
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
resetIdleTimer();
|
||||
|
||||
Response::ResponseCode finalResponseCode = Response::RespOk;
|
||||
for (int i = cont.moderator_command_size() - 1; i >= 0; --i) {
|
||||
Response::ResponseCode resp = Response::RespInvalidCommand;
|
||||
const ModeratorCommand &sc = cont.moderator_command(i);
|
||||
const int num = getPbExtension(sc);
|
||||
logDebugMessage(getSafeDebugString(sc));
|
||||
|
||||
resp = processExtendedModeratorCommand(num, sc, rc);
|
||||
if (resp != Response::RespOk)
|
||||
finalResponseCode = resp;
|
||||
}
|
||||
return finalResponseCode;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::processAdminCommandContainer(const CommandContainer &cont,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
if (!userInfo)
|
||||
return Response::RespLoginNeeded;
|
||||
if (!(userInfo->user_level() & ServerInfo_User::IsAdmin))
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
resetIdleTimer();
|
||||
|
||||
Response::ResponseCode finalResponseCode = Response::RespOk;
|
||||
for (int i = cont.admin_command_size() - 1; i >= 0; --i) {
|
||||
Response::ResponseCode resp = Response::RespInvalidCommand;
|
||||
const AdminCommand &sc = cont.admin_command(i);
|
||||
const int num = getPbExtension(sc);
|
||||
logDebugMessage(getSafeDebugString(sc));
|
||||
|
||||
resp = processExtendedAdminCommand(num, sc, rc);
|
||||
if (resp != Response::RespOk)
|
||||
finalResponseCode = resp;
|
||||
}
|
||||
return finalResponseCode;
|
||||
}
|
||||
|
||||
void Server_ProtocolHandler::processCommandContainer(const CommandContainer &cont)
|
||||
{
|
||||
// Command processing must be disabled after prepareDestroy() has been called.
|
||||
if (deleted)
|
||||
return;
|
||||
|
||||
lastDataReceived = timeRunning;
|
||||
|
||||
ResponseContainer responseContainer(cont.has_cmd_id() ? cont.cmd_id() : -1);
|
||||
Response::ResponseCode finalResponseCode;
|
||||
|
||||
if (cont.game_command_size())
|
||||
finalResponseCode = processGameCommandContainer(cont, responseContainer);
|
||||
else if (cont.room_command_size())
|
||||
finalResponseCode = processRoomCommandContainer(cont, responseContainer);
|
||||
else if (cont.session_command_size())
|
||||
finalResponseCode = processSessionCommandContainer(cont, responseContainer);
|
||||
else if (cont.moderator_command_size())
|
||||
finalResponseCode = processModeratorCommandContainer(cont, responseContainer);
|
||||
else if (cont.admin_command_size())
|
||||
finalResponseCode = processAdminCommandContainer(cont, responseContainer);
|
||||
else
|
||||
finalResponseCode = Response::RespInvalidCommand;
|
||||
|
||||
if ((finalResponseCode != Response::RespNothing))
|
||||
sendResponseContainer(responseContainer, finalResponseCode);
|
||||
}
|
||||
|
||||
void Server_ProtocolHandler::pingClockTimeout()
|
||||
{
|
||||
|
||||
int cmdcountinterval = server->getCommandCountingInterval();
|
||||
int msgcountinterval = server->getMessageCountingInterval();
|
||||
int pingclockinterval = server->getClientKeepAlive();
|
||||
|
||||
int interval = server->getMessageCountingInterval();
|
||||
if (interval > 0) {
|
||||
if (pingclockinterval > 0) {
|
||||
messageSizeOverTime.prepend(0);
|
||||
if (messageSizeOverTime.size() > (msgcountinterval / pingclockinterval))
|
||||
messageSizeOverTime.removeLast();
|
||||
messageCountOverTime.prepend(0);
|
||||
if (messageCountOverTime.size() > (msgcountinterval / pingclockinterval))
|
||||
messageCountOverTime.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
interval = server->getCommandCountingInterval();
|
||||
if (interval > 0) {
|
||||
if (pingclockinterval > 0) {
|
||||
commandCountOverTime.prepend(0);
|
||||
if (commandCountOverTime.size() > (cmdcountinterval / pingclockinterval))
|
||||
commandCountOverTime.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
if (timeRunning - lastDataReceived > server->getMaxPlayerInactivityTime())
|
||||
prepareDestroy();
|
||||
|
||||
// PrivLevel users, Moderators, and Admins are not subject to the server idle timeout policy
|
||||
const bool hasPrivLevel = userInfo && QString::fromStdString(userInfo->privlevel()).toLower() != "none";
|
||||
const bool isModOrAdmin =
|
||||
userInfo && (userInfo->user_level() & (ServerInfo_User::IsModerator | ServerInfo_User::IsAdmin));
|
||||
if (!hasPrivLevel && !isModOrAdmin) {
|
||||
if ((server->getIdleClientTimeout() > 0) && (idleClientWarningSent)) {
|
||||
if (timeRunning - lastActionReceived > server->getIdleClientTimeout()) {
|
||||
prepareDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
if (((timeRunning - lastActionReceived) >= qCeil(server->getIdleClientTimeout() * .9)) &&
|
||||
(!idleClientWarningSent) && (server->getIdleClientTimeout() > 0)) {
|
||||
Event_NotifyUser event;
|
||||
event.set_type(Event_NotifyUser::IDLEWARNING);
|
||||
SessionEvent *se = prepareSessionEvent(event);
|
||||
sendProtocolItem(*se);
|
||||
delete se;
|
||||
idleClientWarningSent = true;
|
||||
}
|
||||
}
|
||||
|
||||
++timeRunning;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::cmdPing(const Command_Ping & /*cmd*/, ResponseContainer & /*rc*/)
|
||||
{
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd, ResponseContainer &rc)
|
||||
{
|
||||
QString userName = nameFromStdString(cmd.user_name()).simplified();
|
||||
QString clientId = nameFromStdString(cmd.clientid()).simplified();
|
||||
QString clientVersion = nameFromStdString(cmd.clientver()).simplified();
|
||||
QString password;
|
||||
bool needsHash = false;
|
||||
if (cmd.has_password()) {
|
||||
if (cmd.password().length() > MAX_NAME_LENGTH)
|
||||
return Response::RespWrongPassword;
|
||||
password = QString::fromStdString(cmd.password());
|
||||
needsHash = true;
|
||||
} else if (cmd.hashed_password().length() > MAX_NAME_LENGTH) {
|
||||
return Response::RespContextError;
|
||||
} else {
|
||||
password = nameFromStdString(cmd.hashed_password());
|
||||
}
|
||||
|
||||
if (userInfo != 0) {
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
// check client feature set against server feature set
|
||||
FeatureSet features;
|
||||
QMap<QString, bool> receivedClientFeatures;
|
||||
QMap<QString, bool> missingClientFeatures;
|
||||
|
||||
int featureCount = qMin(cmd.clientfeatures().size(), MAX_NAME_LENGTH);
|
||||
for (int i = 0; i < featureCount; ++i) {
|
||||
receivedClientFeatures.insert(nameFromStdString(cmd.clientfeatures(i)).simplified(), false);
|
||||
}
|
||||
|
||||
missingClientFeatures =
|
||||
features.identifyMissingFeatures(receivedClientFeatures, server->getServerRequiredFeatureList());
|
||||
|
||||
if (!missingClientFeatures.isEmpty()) {
|
||||
if (features.isRequiredFeaturesMissing(missingClientFeatures, server->getServerRequiredFeatureList())) {
|
||||
Response_Login *re = new Response_Login;
|
||||
re->set_denied_reason_str("Client upgrade required");
|
||||
QMap<QString, bool>::iterator i;
|
||||
for (i = missingClientFeatures.begin(); i != missingClientFeatures.end(); ++i) {
|
||||
re->add_missing_features(i.key().toStdString().c_str());
|
||||
}
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespClientUpdateRequired;
|
||||
}
|
||||
}
|
||||
|
||||
QString reasonStr;
|
||||
int banSecondsLeft = 0;
|
||||
QString connectionType = getConnectionType();
|
||||
AuthenticationResult res = server->loginUser(this, userName, password, needsHash, reasonStr, banSecondsLeft,
|
||||
clientId, clientVersion, connectionType);
|
||||
switch (res) {
|
||||
case UserIsBanned: {
|
||||
Response_Login *re = new Response_Login;
|
||||
re->set_denied_reason_str(reasonStr.toStdString());
|
||||
if (banSecondsLeft != 0)
|
||||
re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsLeft).toSecsSinceEpoch());
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespUserIsBanned;
|
||||
}
|
||||
case NotLoggedIn:
|
||||
return Response::RespWrongPassword;
|
||||
case WouldOverwriteOldSession:
|
||||
return Response::RespWouldOverwriteOldSession;
|
||||
case UsernameInvalid: {
|
||||
Response_Login *re = new Response_Login;
|
||||
re->set_denied_reason_str(reasonStr.toStdString());
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespUsernameInvalid;
|
||||
}
|
||||
case RegistrationRequired:
|
||||
return Response::RespRegistrationRequired;
|
||||
case ClientIdRequired:
|
||||
return Response::RespClientIdRequired;
|
||||
case UserIsInactive:
|
||||
return Response::RespAccountNotActivated;
|
||||
default:
|
||||
authState = res;
|
||||
usingRealPassword = needsHash;
|
||||
}
|
||||
|
||||
// limit the number of non-privileged users that can connect to the server based on configuration settings
|
||||
if (!userInfo || QString::fromStdString(userInfo->privlevel()).toLower() == "none") {
|
||||
if (server->getMaxUserLimitEnabled()) {
|
||||
if (server->getUsersCount() > server->getMaxUserTotal()) {
|
||||
qDebug() << "Max Users Total Limit Reached, please increase the max_users_total setting.";
|
||||
return Response::RespServerFull;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userName = QString::fromStdString(userInfo->name());
|
||||
Event_ServerMessage event;
|
||||
event.set_message(server->getLoginMessage().toStdString());
|
||||
rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event));
|
||||
|
||||
Response_Login *re = new Response_Login;
|
||||
re->mutable_user_info()->CopyFrom(copyUserInfo(true));
|
||||
|
||||
if (authState == PasswordRight) {
|
||||
QMapIterator<QString, ServerInfo_User> buddyIterator(databaseInterface->getBuddyList(userName));
|
||||
while (buddyIterator.hasNext())
|
||||
re->add_buddy_list()->CopyFrom(buddyIterator.next().value());
|
||||
|
||||
QMapIterator<QString, ServerInfo_User> ignoreIterator(databaseInterface->getIgnoreList(userName));
|
||||
while (ignoreIterator.hasNext())
|
||||
re->add_ignore_list()->CopyFrom(ignoreIterator.next().value());
|
||||
}
|
||||
|
||||
// return to client any missing features the server has that the client does not
|
||||
if (!missingClientFeatures.isEmpty()) {
|
||||
QMap<QString, bool>::iterator i;
|
||||
for (i = missingClientFeatures.begin(); i != missingClientFeatures.end(); ++i)
|
||||
re->add_missing_features(i.key().toStdString().c_str());
|
||||
}
|
||||
|
||||
joinPersistentGames(rc);
|
||||
databaseInterface->removeForgotPassword(userName);
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message &cmd, ResponseContainer &rc)
|
||||
{
|
||||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
QReadLocker locker(&server->clientsLock);
|
||||
|
||||
QString receiver = nameFromStdString(cmd.user_name());
|
||||
Server_AbstractUserInterface *userInterface = server->findUser(receiver);
|
||||
if (!userInterface) {
|
||||
return Response::RespNameNotFound;
|
||||
}
|
||||
if (databaseInterface->isInIgnoreList(receiver, QString::fromStdString(userInfo->name()))) {
|
||||
return Response::RespInIgnoreList;
|
||||
}
|
||||
if (!addSaidMessageSize(static_cast<int>(cmd.message().size()))) {
|
||||
return Response::RespChatFlood;
|
||||
}
|
||||
|
||||
Event_UserMessage event;
|
||||
event.set_sender_name(userInfo->name());
|
||||
event.set_receiver_name(receiver.toStdString());
|
||||
event.set_message(cmd.message());
|
||||
|
||||
SessionEvent *se = prepareSessionEvent(event);
|
||||
userInterface->sendProtocolItem(*se);
|
||||
rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, se);
|
||||
|
||||
databaseInterface->logMessage(userInfo->id(), QString::fromStdString(userInfo->name()),
|
||||
QString::fromStdString(userInfo->address()), QString::fromStdString(cmd.message()),
|
||||
Server_DatabaseInterface::MessageTargetChat, userInterface->getUserInfo()->id(),
|
||||
receiver);
|
||||
resetIdleTimer();
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
// Do not show games to someone on the ignore list of that user, except for mods
|
||||
QString target_user = nameFromStdString(cmd.user_name());
|
||||
Server_AbstractUserInterface *userInterface = server->findUser(target_user);
|
||||
if (!userInterface) {
|
||||
return Response::RespNameNotFound;
|
||||
}
|
||||
if (!(userInfo->user_level() & (ServerInfo_User::IsModerator | ServerInfo_User::IsAdmin)) &&
|
||||
databaseInterface->isInIgnoreList(target_user, QString::fromStdString(userInfo->name()))) {
|
||||
return Response::RespInIgnoreList;
|
||||
}
|
||||
|
||||
// We don't need to check whether the user is logged in; persistent games should also work.
|
||||
// The client needs to deal with an empty result list.
|
||||
|
||||
Response_GetGamesOfUser *re = new Response_GetGamesOfUser;
|
||||
server->roomsLock.lockForRead();
|
||||
QMapIterator<int, Server_Room *> roomIterator(server->getRooms());
|
||||
while (roomIterator.hasNext()) {
|
||||
Server_Room *room = roomIterator.next().value();
|
||||
room->gamesLock.lockForRead();
|
||||
room->getInfo(*re->add_room_list(), false, true);
|
||||
QListIterator<ServerInfo_Game> gameIterator(room->getGamesOfUser(nameFromStdString(cmd.user_name())));
|
||||
while (gameIterator.hasNext())
|
||||
re->add_game_list()->CopyFrom(gameIterator.next());
|
||||
room->gamesLock.unlock();
|
||||
}
|
||||
server->roomsLock.unlock();
|
||||
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc)
|
||||
{
|
||||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
QString userName = nameFromStdString(cmd.user_name());
|
||||
Response_GetUserInfo *re = new Response_GetUserInfo;
|
||||
if (userName.isEmpty())
|
||||
re->mutable_user_info()->CopyFrom(*userInfo);
|
||||
else {
|
||||
|
||||
QReadLocker locker(&server->clientsLock);
|
||||
|
||||
ServerInfo_User_Container *infoSource = server->findUser(userName);
|
||||
if (!infoSource) {
|
||||
re->mutable_user_info()->CopyFrom(databaseInterface->getUserData(userName, true));
|
||||
} else {
|
||||
re->mutable_user_info()->CopyFrom(
|
||||
infoSource->copyUserInfo(true, false, userInfo->user_level() & ServerInfo_User::IsModerator));
|
||||
}
|
||||
}
|
||||
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::cmdListRooms(const Command_ListRooms & /*cmd*/, ResponseContainer &rc)
|
||||
{
|
||||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
Event_ListRooms event;
|
||||
QMapIterator<int, Server_Room *> roomIterator(server->getRooms());
|
||||
while (roomIterator.hasNext())
|
||||
roomIterator.next().value()->getInfo(*event.add_room_list(), false);
|
||||
rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event));
|
||||
|
||||
acceptsRoomListChanges = true;
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::cmdJoinRoom(const Command_JoinRoom &cmd, ResponseContainer &rc)
|
||||
{
|
||||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
if (rooms.contains(cmd.room_id()))
|
||||
return Response::RespContextError;
|
||||
|
||||
QReadLocker serverLocker(&server->roomsLock);
|
||||
Server_Room *room = server->getRooms().value(cmd.room_id(), 0);
|
||||
if (!room)
|
||||
return Response::RespNameNotFound;
|
||||
|
||||
if (!(userInfo->user_level() & ServerInfo_User::IsModerator))
|
||||
if (!(room->userMayJoin(*userInfo)))
|
||||
return Response::RespUserLevelTooLow;
|
||||
|
||||
room->addClient(this);
|
||||
rooms.insert(room->getId(), room);
|
||||
|
||||
QReadLocker chatHistoryLocker(&room->historyLock);
|
||||
QList<ServerInfo_ChatMessage> chatHistory = room->getChatHistory();
|
||||
ServerInfo_ChatMessage chatMessage;
|
||||
for (int i = 0; i < chatHistory.size(); ++i) {
|
||||
chatMessage = chatHistory.at(i);
|
||||
Event_RoomSay roomChatHistory;
|
||||
roomChatHistory.set_message(chatMessage.sender_name() + ": " + chatMessage.message());
|
||||
roomChatHistory.set_message_type(Event_RoomSay::ChatHistory);
|
||||
roomChatHistory.set_time_of(
|
||||
QDateTime::fromString(QString::fromStdString(chatMessage.time())).toMSecsSinceEpoch());
|
||||
rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, room->prepareRoomEvent(roomChatHistory));
|
||||
}
|
||||
|
||||
Event_RoomSay joinMessageEvent;
|
||||
joinMessageEvent.set_message(room->getJoinMessage().toStdString());
|
||||
joinMessageEvent.set_message_type(Event_RoomSay::Welcome);
|
||||
rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, room->prepareRoomEvent(joinMessageEvent));
|
||||
|
||||
Response_JoinRoom *re = new Response_JoinRoom;
|
||||
room->getInfo(*re->mutable_room_info(), true);
|
||||
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_ProtocolHandler::cmdListUsers(const Command_ListUsers & /*cmd*/, ResponseContainer &rc)
|
||||
{
|
||||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
Response_ListUsers *re = new Response_ListUsers;
|
||||
server->clientsLock.lockForRead();
|
||||
QMapIterator<QString, Server_ProtocolHandler *> userIterator = server->getUsers();
|
||||
while (userIterator.hasNext())
|
||||
re->add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false));
|
||||
QMapIterator<QString, Server_AbstractUserInterface *> extIterator = server->getExternalUsers();
|
||||
while (extIterator.hasNext())
|
||||
re->add_user_list()->CopyFrom(extIterator.next().value()->copyUserInfo(false));
|
||||
|
||||
acceptsUserListChanges = true;
|
||||
server->clientsLock.unlock();
|
||||
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode
|
||||
Server_ProtocolHandler::cmdLeaveRoom(const Command_LeaveRoom & /*cmd*/, Server_Room *room, ResponseContainer & /*rc*/)
|
||||
{
|
||||
rooms.remove(room->getId());
|
||||
room->removeClient(this);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
bool Server_ProtocolHandler::addSaidMessageSize(int size)
|
||||
{
|
||||
if (server->getMessageCountingInterval() <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int totalSize = 0, totalCount = 0;
|
||||
if (messageSizeOverTime.isEmpty()) {
|
||||
messageSizeOverTime.prepend(0);
|
||||
}
|
||||
|
||||
messageSizeOverTime[0] += size;
|
||||
for (int messageSize : messageSizeOverTime) {
|
||||
totalSize += messageSize;
|
||||
}
|
||||
|
||||
if (messageCountOverTime.isEmpty()) {
|
||||
messageCountOverTime.prepend(0);
|
||||
}
|
||||
|
||||
messageCountOverTime[0] += 1;
|
||||
for (int messageCount : messageCountOverTime) {
|
||||
totalCount += messageCount;
|
||||
}
|
||||
|
||||
return totalSize <= server->getMaxMessageSizePerInterval() && totalCount <= server->getMaxMessageCountPerInterval();
|
||||
}
|
||||
|
||||
Response::ResponseCode
|
||||
Server_ProtocolHandler::cmdRoomSay(const Command_RoomSay &cmd, Server_Room *room, ResponseContainer & /*rc*/)
|
||||
{
|
||||
if (!addSaidMessageSize(static_cast<int>(cmd.message().size()))) {
|
||||
return Response::RespChatFlood;
|
||||
}
|
||||
QString msg = QString::fromStdString(cmd.message());
|
||||
|
||||
msg.replace(QChar('\n'), QChar(' '));
|
||||
|
||||
room->say(QString::fromStdString(userInfo->name()), msg);
|
||||
|
||||
databaseInterface->logMessage(userInfo->id(), QString::fromStdString(userInfo->name()),
|
||||
QString::fromStdString(userInfo->address()), msg,
|
||||
Server_DatabaseInterface::MessageTargetRoom, room->getId(), room->getName());
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode
|
||||
Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room *room, ResponseContainer &rc)
|
||||
{
|
||||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
if (cmd.password().length() > MAX_NAME_LENGTH)
|
||||
return Response::RespContextError;
|
||||
|
||||
auto level = userInfo->user_level();
|
||||
bool isJudge = level & ServerInfo_User::IsJudge;
|
||||
int maxGames = server->getMaxGamesPerUser();
|
||||
bool asJudge = cmd.join_as_judge();
|
||||
bool asSpectator = cmd.join_as_spectator();
|
||||
// allow judges to open games as spectator without limit to facilitate bots etc, -1 means no limit
|
||||
if (!(isJudge && asJudge && asSpectator) && maxGames >= 0 &&
|
||||
room->getGamesCreatedByUser(QString::fromStdString(userInfo->name())) >= maxGames) {
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
// if a non judge user tries to create a game as judge while not permitted, instead create a normal game
|
||||
if (asJudge && !(server->permitCreateGameAsJudge() || isJudge)) {
|
||||
asJudge = false;
|
||||
}
|
||||
|
||||
QList<int> gameTypes;
|
||||
int gameTypeCount = qMin(cmd.game_type_ids().size(), MAX_NAME_LENGTH);
|
||||
for (int i = 0; i < gameTypeCount; ++i) { // the client actually only sends one of these
|
||||
gameTypes.append(cmd.game_type_ids(i));
|
||||
}
|
||||
|
||||
QString description = nameFromStdString(cmd.description());
|
||||
int startingLifeTotal = cmd.has_starting_life_total() ? cmd.starting_life_total() : 20;
|
||||
|
||||
bool shareDecklistsOnLoad = cmd.has_share_decklists_on_load() ? cmd.share_decklists_on_load() : false;
|
||||
|
||||
const int gameId = databaseInterface->getNextGameId();
|
||||
if (gameId == -1) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
// When server doesn't permit registered users to exist, do not honor only-reg setting
|
||||
bool onlyRegisteredUsers = cmd.only_registered() && (server->permitUnregisteredUsers());
|
||||
Server_Game *game = new Server_Game(
|
||||
copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()), cmd.max_players(), gameTypes,
|
||||
cmd.only_buddies(), onlyRegisteredUsers, cmd.spectators_allowed(), cmd.spectators_need_password(),
|
||||
cmd.spectators_can_talk(), cmd.spectators_see_everything(), startingLifeTotal, shareDecklistsOnLoad, room);
|
||||
|
||||
game->addPlayer(this, rc, asSpectator, asJudge, false);
|
||||
room->addGame(game);
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode
|
||||
Server_ProtocolHandler::cmdJoinGame(const Command_JoinGame &cmd, Server_Room *room, ResponseContainer &rc)
|
||||
{
|
||||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
return room->processJoinGameCommand(cmd, rc, this);
|
||||
}
|
||||
|
||||
void Server_ProtocolHandler::resetIdleTimer()
|
||||
{
|
||||
lastActionReceived = timeRunning;
|
||||
idleClientWarningSent = false;
|
||||
}
|
||||
141
common/server/server_protocolhandler.h
Normal file
141
common/server/server_protocolhandler.h
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#ifndef SERVER_PROTOCOLHANDLER_H
|
||||
#define SERVER_PROTOCOLHANDLER_H
|
||||
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/server_message.pb.h"
|
||||
#include "server.h"
|
||||
#include "server_abstractuserinterface.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
|
||||
class Features;
|
||||
class Server_DatabaseInterface;
|
||||
class Server_Player;
|
||||
class ServerInfo_User;
|
||||
class Server_Room;
|
||||
class QTimer;
|
||||
class FeatureSet;
|
||||
|
||||
class ServerMessage;
|
||||
class Response;
|
||||
class SessionEvent;
|
||||
class GameEventContainer;
|
||||
class RoomEvent;
|
||||
class ResponseContainer;
|
||||
|
||||
class CommandContainer;
|
||||
class SessionCommand;
|
||||
class ModeratorCommand;
|
||||
class AdminCommand;
|
||||
|
||||
class Command_Ping;
|
||||
class Command_Login;
|
||||
class Command_Register;
|
||||
class Command_Message;
|
||||
class Command_ListUsers;
|
||||
class Command_GetGamesOfUser;
|
||||
class Command_GetUserInfo;
|
||||
class Command_ListRooms;
|
||||
class Command_JoinRoom;
|
||||
class Command_LeaveRoom;
|
||||
class Command_RoomSay;
|
||||
class Command_CreateGame;
|
||||
class Command_JoinGame;
|
||||
|
||||
class Server_ProtocolHandler : public QObject, public Server_AbstractUserInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
QMap<int, Server_Room *> rooms;
|
||||
|
||||
bool deleted;
|
||||
Server_DatabaseInterface *databaseInterface;
|
||||
AuthenticationResult authState;
|
||||
bool usingRealPassword;
|
||||
bool acceptsUserListChanges;
|
||||
bool acceptsRoomListChanges;
|
||||
bool idleClientWarningSent;
|
||||
virtual void logDebugMessage(const QString & /* message */)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
QList<int> messageSizeOverTime, messageCountOverTime, commandCountOverTime;
|
||||
int timeRunning, lastDataReceived, lastActionReceived;
|
||||
|
||||
virtual void transmitProtocolItem(const ServerMessage &item) = 0;
|
||||
|
||||
Response::ResponseCode cmdPing(const Command_Ping &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdLogin(const Command_Login &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdMessage(const Command_Message &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdListRooms(const Command_ListRooms &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdJoinRoom(const Command_JoinRoom &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdListUsers(const Command_ListUsers &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdLeaveRoom(const Command_LeaveRoom &cmd, Server_Room *room, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdRoomSay(const Command_RoomSay &cmd, Server_Room *room, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdCreateGame(const Command_CreateGame &cmd, Server_Room *room, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdJoinGame(const Command_JoinGame &cmd, Server_Room *room, ResponseContainer &rc);
|
||||
|
||||
Response::ResponseCode processSessionCommandContainer(const CommandContainer &cont, ResponseContainer &rc);
|
||||
virtual Response::ResponseCode
|
||||
processExtendedSessionCommand(int /* cmdType */, const SessionCommand & /* cmd */, ResponseContainer & /* rc */)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
Response::ResponseCode processRoomCommandContainer(const CommandContainer &cont, ResponseContainer &rc);
|
||||
Response::ResponseCode processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc);
|
||||
Response::ResponseCode processModeratorCommandContainer(const CommandContainer &cont, ResponseContainer &rc);
|
||||
virtual Response::ResponseCode
|
||||
processExtendedModeratorCommand(int /* cmdType */, const ModeratorCommand & /* cmd */, ResponseContainer & /* rc */)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
Response::ResponseCode processAdminCommandContainer(const CommandContainer &cont, ResponseContainer &rc);
|
||||
virtual Response::ResponseCode
|
||||
processExtendedAdminCommand(int /* cmdType */, const AdminCommand & /* cmd */, ResponseContainer & /* rc */)
|
||||
{
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
void resetIdleTimer();
|
||||
private slots:
|
||||
void pingClockTimeout();
|
||||
public slots:
|
||||
void prepareDestroy();
|
||||
|
||||
public:
|
||||
Server_ProtocolHandler(Server *_server, Server_DatabaseInterface *_databaseInterface, QObject *parent = 0);
|
||||
~Server_ProtocolHandler();
|
||||
|
||||
bool getAcceptsUserListChanges() const
|
||||
{
|
||||
return acceptsUserListChanges;
|
||||
}
|
||||
bool getAcceptsRoomListChanges() const
|
||||
{
|
||||
return acceptsRoomListChanges;
|
||||
}
|
||||
virtual QString getAddress() const = 0;
|
||||
virtual QString getConnectionType() const = 0;
|
||||
Server_DatabaseInterface *getDatabaseInterface() const
|
||||
{
|
||||
return databaseInterface;
|
||||
}
|
||||
|
||||
int getLastCommandTime() const
|
||||
{
|
||||
return timeRunning - lastDataReceived;
|
||||
}
|
||||
bool addSaidMessageSize(int size);
|
||||
void processCommandContainer(const CommandContainer &cont);
|
||||
|
||||
void sendProtocolItem(const Response &item);
|
||||
void sendProtocolItem(const SessionEvent &item);
|
||||
void sendProtocolItem(const GameEventContainer &item);
|
||||
void sendProtocolItem(const RoomEvent &item);
|
||||
};
|
||||
|
||||
#endif
|
||||
24
common/server/server_remoteuserinterface.cpp
Normal file
24
common/server/server_remoteuserinterface.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#include "server_remoteuserinterface.h"
|
||||
|
||||
#include "pb/serverinfo_user.pb.h"
|
||||
#include "server.h"
|
||||
|
||||
void Server_RemoteUserInterface::sendProtocolItem(const Response &item)
|
||||
{
|
||||
server->sendIsl_Response(item, userInfo->server_id(), userInfo->session_id());
|
||||
}
|
||||
|
||||
void Server_RemoteUserInterface::sendProtocolItem(const SessionEvent &item)
|
||||
{
|
||||
server->sendIsl_SessionEvent(item, userInfo->server_id(), userInfo->session_id());
|
||||
}
|
||||
|
||||
void Server_RemoteUserInterface::sendProtocolItem(const GameEventContainer &item)
|
||||
{
|
||||
server->sendIsl_GameEventContainer(item, userInfo->server_id(), userInfo->session_id());
|
||||
}
|
||||
|
||||
void Server_RemoteUserInterface::sendProtocolItem(const RoomEvent &item)
|
||||
{
|
||||
server->sendIsl_RoomEvent(item, userInfo->server_id(), userInfo->session_id());
|
||||
}
|
||||
29
common/server/server_remoteuserinterface.h
Normal file
29
common/server/server_remoteuserinterface.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef SERVER_REMOTEUSERINTERFACE_H
|
||||
#define SERVER_REMOTEUSERINTERFACE_H
|
||||
|
||||
#include "server_abstractuserinterface.h"
|
||||
|
||||
class Server_RemoteUserInterface : public Server_AbstractUserInterface
|
||||
{
|
||||
public:
|
||||
Server_RemoteUserInterface(Server *_server, const ServerInfo_User_Container &_userInfoContainer)
|
||||
: Server_AbstractUserInterface(_server, _userInfoContainer)
|
||||
{
|
||||
}
|
||||
|
||||
int getLastCommandTime() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
bool addSaidMessageSize(int /*size*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void sendProtocolItem(const Response &item);
|
||||
void sendProtocolItem(const SessionEvent &item);
|
||||
void sendProtocolItem(const GameEventContainer &item);
|
||||
void sendProtocolItem(const RoomEvent &item);
|
||||
};
|
||||
|
||||
#endif
|
||||
95
common/server/server_response_containers.cpp
Normal file
95
common/server/server_response_containers.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#include "server_response_containers.h"
|
||||
|
||||
#include "game/server_game.h"
|
||||
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
GameEventStorageItem::GameEventStorageItem(const ::google::protobuf::Message &_event,
|
||||
int _playerId,
|
||||
EventRecipients _recipients)
|
||||
: event(new GameEvent), recipients(_recipients)
|
||||
{
|
||||
event->GetReflection()->MutableMessage(event, _event.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(_event);
|
||||
event->set_player_id(_playerId);
|
||||
}
|
||||
|
||||
GameEventStorageItem::~GameEventStorageItem()
|
||||
{
|
||||
delete event;
|
||||
}
|
||||
|
||||
GameEventStorage::GameEventStorage() : gameEventContext(0), privatePlayerId(0)
|
||||
{
|
||||
}
|
||||
|
||||
GameEventStorage::~GameEventStorage()
|
||||
{
|
||||
delete gameEventContext;
|
||||
for (int i = 0; i < gameEventList.size(); ++i)
|
||||
delete gameEventList[i];
|
||||
}
|
||||
|
||||
void GameEventStorage::setGameEventContext(const ::google::protobuf::Message &_gameEventContext)
|
||||
{
|
||||
delete gameEventContext;
|
||||
gameEventContext = new GameEventContext;
|
||||
gameEventContext->GetReflection()
|
||||
->MutableMessage(gameEventContext, _gameEventContext.GetDescriptor()->FindExtensionByName("ext"))
|
||||
->CopyFrom(_gameEventContext);
|
||||
}
|
||||
|
||||
void GameEventStorage::enqueueGameEvent(const ::google::protobuf::Message &event,
|
||||
int playerId,
|
||||
GameEventStorageItem::EventRecipients recipients,
|
||||
int _privatePlayerId)
|
||||
{
|
||||
gameEventList.append(new GameEventStorageItem(event, playerId, recipients));
|
||||
if (_privatePlayerId != -1)
|
||||
privatePlayerId = _privatePlayerId;
|
||||
}
|
||||
|
||||
void GameEventStorage::sendToGame(Server_Game *game)
|
||||
{
|
||||
if (gameEventList.isEmpty())
|
||||
return;
|
||||
|
||||
auto *contPrivate = new GameEventContainer;
|
||||
auto *contOthers = new GameEventContainer;
|
||||
int id = privatePlayerId;
|
||||
if (forcedByJudge != -1) {
|
||||
contPrivate->set_forced_by_judge(forcedByJudge);
|
||||
contOthers->set_forced_by_judge(forcedByJudge);
|
||||
if (overwriteOwnership) {
|
||||
id = forcedByJudge;
|
||||
setOverwriteOwnership(false);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &i : gameEventList) {
|
||||
const GameEvent &event = i->getGameEvent();
|
||||
const GameEventStorageItem::EventRecipients recipients = i->getRecipients();
|
||||
if (recipients.testFlag(GameEventStorageItem::SendToPrivate))
|
||||
contPrivate->add_event_list()->CopyFrom(event);
|
||||
if (recipients.testFlag(GameEventStorageItem::SendToOthers))
|
||||
contOthers->add_event_list()->CopyFrom(event);
|
||||
}
|
||||
if (gameEventContext) {
|
||||
contPrivate->mutable_context()->CopyFrom(*gameEventContext);
|
||||
contOthers->mutable_context()->CopyFrom(*gameEventContext);
|
||||
}
|
||||
game->sendGameEventContainer(contPrivate, GameEventStorageItem::SendToPrivate, id);
|
||||
game->sendGameEventContainer(contOthers, GameEventStorageItem::SendToOthers, id);
|
||||
}
|
||||
|
||||
ResponseContainer::ResponseContainer(int _cmdId) : cmdId(_cmdId), responseExtension(0)
|
||||
{
|
||||
}
|
||||
|
||||
ResponseContainer::~ResponseContainer()
|
||||
{
|
||||
delete responseExtension;
|
||||
for (int i = 0; i < preResponseQueue.size(); ++i)
|
||||
delete preResponseQueue[i].second;
|
||||
for (int i = 0; i < postResponseQueue.size(); ++i)
|
||||
delete postResponseQueue[i].second;
|
||||
}
|
||||
130
common/server/server_response_containers.h
Normal file
130
common/server/server_response_containers.h
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#ifndef SERVER_RESPONSE_CONTAINERS_H
|
||||
#define SERVER_RESPONSE_CONTAINERS_H
|
||||
|
||||
#include "pb/server_message.pb.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
|
||||
namespace google
|
||||
{
|
||||
namespace protobuf
|
||||
{
|
||||
class Message;
|
||||
}
|
||||
} // namespace google
|
||||
class Server_Game;
|
||||
|
||||
class GameEventStorageItem
|
||||
{
|
||||
public:
|
||||
enum EventRecipient
|
||||
{
|
||||
SendToPrivate = 0x01,
|
||||
SendToOthers = 0x02
|
||||
};
|
||||
Q_DECLARE_FLAGS(EventRecipients, EventRecipient)
|
||||
private:
|
||||
GameEvent *event;
|
||||
EventRecipients recipients;
|
||||
|
||||
public:
|
||||
GameEventStorageItem(const ::google::protobuf::Message &_event, int _playerId, EventRecipients _recipients);
|
||||
~GameEventStorageItem();
|
||||
|
||||
const GameEvent &getGameEvent() const
|
||||
{
|
||||
return *event;
|
||||
}
|
||||
EventRecipients getRecipients() const
|
||||
{
|
||||
return recipients;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(GameEventStorageItem::EventRecipients)
|
||||
|
||||
class GameEventStorage
|
||||
{
|
||||
private:
|
||||
::google::protobuf::Message *gameEventContext;
|
||||
QList<GameEventStorageItem *> gameEventList;
|
||||
int privatePlayerId;
|
||||
int forcedByJudge = -1;
|
||||
bool overwriteOwnership = false;
|
||||
|
||||
public:
|
||||
GameEventStorage();
|
||||
~GameEventStorage();
|
||||
|
||||
void setGameEventContext(const ::google::protobuf::Message &_gameEventContext);
|
||||
::google::protobuf::Message *getGameEventContext() const
|
||||
{
|
||||
return gameEventContext;
|
||||
}
|
||||
const QList<GameEventStorageItem *> &getGameEventList() const
|
||||
{
|
||||
return gameEventList;
|
||||
}
|
||||
int getPrivatePlayerId() const
|
||||
{
|
||||
return privatePlayerId;
|
||||
}
|
||||
void setForcedByJudge(int playerId)
|
||||
{
|
||||
forcedByJudge = playerId;
|
||||
}
|
||||
void setOverwriteOwnership(bool shouldOverwriteOwnership)
|
||||
{
|
||||
overwriteOwnership = shouldOverwriteOwnership;
|
||||
}
|
||||
|
||||
void enqueueGameEvent(const ::google::protobuf::Message &event,
|
||||
int playerId,
|
||||
GameEventStorageItem::EventRecipients recipients = GameEventStorageItem::SendToPrivate |
|
||||
GameEventStorageItem::SendToOthers,
|
||||
int _privatePlayerId = -1);
|
||||
void sendToGame(Server_Game *game);
|
||||
};
|
||||
|
||||
class ResponseContainer
|
||||
{
|
||||
private:
|
||||
int cmdId;
|
||||
::google::protobuf::Message *responseExtension;
|
||||
QList<QPair<ServerMessage::MessageType, ::google::protobuf::Message *>> preResponseQueue, postResponseQueue;
|
||||
|
||||
public:
|
||||
ResponseContainer(int _cmdId);
|
||||
~ResponseContainer();
|
||||
|
||||
int getCmdId() const
|
||||
{
|
||||
return cmdId;
|
||||
}
|
||||
void setResponseExtension(::google::protobuf::Message *_responseExtension)
|
||||
{
|
||||
responseExtension = _responseExtension;
|
||||
}
|
||||
::google::protobuf::Message *getResponseExtension() const
|
||||
{
|
||||
return responseExtension;
|
||||
}
|
||||
void enqueuePreResponseItem(ServerMessage::MessageType type, ::google::protobuf::Message *item)
|
||||
{
|
||||
preResponseQueue.append(qMakePair(type, item));
|
||||
}
|
||||
void enqueuePostResponseItem(ServerMessage::MessageType type, ::google::protobuf::Message *item)
|
||||
{
|
||||
postResponseQueue.append(qMakePair(type, item));
|
||||
}
|
||||
const QList<QPair<ServerMessage::MessageType, ::google::protobuf::Message *>> &getPreResponseQueue() const
|
||||
{
|
||||
return preResponseQueue;
|
||||
}
|
||||
const QList<QPair<ServerMessage::MessageType, ::google::protobuf::Message *>> &getPostResponseQueue() const
|
||||
{
|
||||
return postResponseQueue;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
429
common/server/server_room.cpp
Normal file
429
common/server/server_room.cpp
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
#include "server_room.h"
|
||||
|
||||
#include "../trice_limits.h"
|
||||
#include "game/server_game.h"
|
||||
#include "pb/commands.pb.h"
|
||||
#include "pb/event_join_room.pb.h"
|
||||
#include "pb/event_leave_room.pb.h"
|
||||
#include "pb/event_list_games.pb.h"
|
||||
#include "pb/event_remove_messages.pb.h"
|
||||
#include "pb/event_room_say.pb.h"
|
||||
#include "pb/room_commands.pb.h"
|
||||
#include "pb/serverinfo_chat_message.pb.h"
|
||||
#include "pb/serverinfo_room.pb.h"
|
||||
#include "server_protocolhandler.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
Server_Room::Server_Room(int _id,
|
||||
int _chatHistorySize,
|
||||
const QString &_name,
|
||||
const QString &_description,
|
||||
const QString &_permissionLevel,
|
||||
const QString &_privilegeLevel,
|
||||
bool _autoJoin,
|
||||
const QString &_joinMessage,
|
||||
const QStringList &_gameTypes,
|
||||
Server *parent)
|
||||
: QObject(parent), id(_id), chatHistorySize(_chatHistorySize), name(_name), description(_description),
|
||||
permissionLevel(_permissionLevel), privilegeLevel(_privilegeLevel), autoJoin(_autoJoin),
|
||||
joinMessage(_joinMessage), gameTypes(_gameTypes), gamesLock(QReadWriteLock::Recursive)
|
||||
{
|
||||
connect(
|
||||
this, &Server_Room::gameListChanged, this, [this](auto gameInfo) { broadcastGameListUpdate(gameInfo); },
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
Server_Room::~Server_Room()
|
||||
{
|
||||
qDebug("Server_Room destructor");
|
||||
|
||||
gamesLock.lockForWrite();
|
||||
const QList<Server_Game *> gameList = games.values();
|
||||
for (int i = 0; i < gameList.size(); ++i)
|
||||
delete gameList[i];
|
||||
games.clear();
|
||||
gamesLock.unlock();
|
||||
|
||||
usersLock.lockForWrite();
|
||||
users.clear();
|
||||
usersLock.unlock();
|
||||
}
|
||||
|
||||
bool Server_Room::userMayJoin(const ServerInfo_User &userInfo)
|
||||
{
|
||||
|
||||
if (permissionLevel.toLower() == "administrator" || permissionLevel.toLower() == "moderator")
|
||||
return false;
|
||||
|
||||
if (permissionLevel.toLower() == "registered" && !(userInfo.user_level() & ServerInfo_User::IsRegistered))
|
||||
return false;
|
||||
|
||||
if (privilegeLevel.toLower() != "none") {
|
||||
if (privilegeLevel.toLower() == "privileged") {
|
||||
if (privilegeLevel.toLower() == "none")
|
||||
return false;
|
||||
} else {
|
||||
if (privilegeLevel.toLower() != QString::fromStdString(userInfo.privlevel()).toLower())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Server *Server_Room::getServer() const
|
||||
{
|
||||
return static_cast<Server *>(parent());
|
||||
}
|
||||
|
||||
const ServerInfo_Room &
|
||||
Server_Room::getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes, bool includeExternalData) const
|
||||
{
|
||||
result.set_room_id(id);
|
||||
result.set_name(name.toStdString());
|
||||
result.set_description(description.toStdString());
|
||||
result.set_auto_join(autoJoin);
|
||||
result.set_permissionlevel(permissionLevel.toStdString());
|
||||
result.set_privilegelevel(privilegeLevel.toStdString());
|
||||
|
||||
gamesLock.lockForRead();
|
||||
result.set_game_count(games.size() + externalGames.size());
|
||||
if (complete) {
|
||||
QMapIterator<int, Server_Game *> gameIterator(games);
|
||||
while (gameIterator.hasNext())
|
||||
gameIterator.next().value()->getInfo(*result.add_game_list());
|
||||
if (includeExternalData) {
|
||||
QMapIterator<int, ServerInfo_Game> externalGameIterator(externalGames);
|
||||
while (externalGameIterator.hasNext())
|
||||
result.add_game_list()->CopyFrom(externalGameIterator.next().value());
|
||||
}
|
||||
}
|
||||
gamesLock.unlock();
|
||||
|
||||
usersLock.lockForRead();
|
||||
result.set_player_count(users.size() + externalUsers.size());
|
||||
if (complete) {
|
||||
QMapIterator<QString, Server_ProtocolHandler *> userIterator(users);
|
||||
while (userIterator.hasNext())
|
||||
result.add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false));
|
||||
if (includeExternalData) {
|
||||
QMapIterator<QString, ServerInfo_User_Container> externalUserIterator(externalUsers);
|
||||
while (externalUserIterator.hasNext())
|
||||
result.add_user_list()->CopyFrom(externalUserIterator.next().value().copyUserInfo(false));
|
||||
}
|
||||
}
|
||||
usersLock.unlock();
|
||||
|
||||
if (complete || showGameTypes)
|
||||
for (int i = 0; i < gameTypes.size(); ++i) {
|
||||
ServerInfo_GameType *gameTypeInfo = result.add_gametype_list();
|
||||
gameTypeInfo->set_game_type_id(i);
|
||||
gameTypeInfo->set_description(gameTypes[i].toStdString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
RoomEvent *Server_Room::prepareRoomEvent(const ::google::protobuf::Message &roomEvent)
|
||||
{
|
||||
RoomEvent *event = new RoomEvent;
|
||||
event->set_room_id(id);
|
||||
event->GetReflection()
|
||||
->MutableMessage(event, roomEvent.GetDescriptor()->FindExtensionByName("ext"))
|
||||
->CopyFrom(roomEvent);
|
||||
return event;
|
||||
}
|
||||
|
||||
void Server_Room::addClient(Server_ProtocolHandler *client)
|
||||
{
|
||||
Event_JoinRoom event;
|
||||
event.mutable_user_info()->CopyFrom(client->copyUserInfo(false));
|
||||
sendRoomEvent(prepareRoomEvent(event));
|
||||
|
||||
ServerInfo_Room roomInfo;
|
||||
roomInfo.set_room_id(id);
|
||||
|
||||
usersLock.lockForWrite();
|
||||
users.insert(QString::fromStdString(client->getUserInfo()->name()), client);
|
||||
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||
usersLock.unlock();
|
||||
|
||||
// XXX This can be removed during the next client update.
|
||||
gamesLock.lockForRead();
|
||||
roomInfo.set_game_count(games.size() + externalGames.size());
|
||||
gamesLock.unlock();
|
||||
// -----------
|
||||
|
||||
emit roomInfoChanged(roomInfo);
|
||||
}
|
||||
|
||||
void Server_Room::removeClient(Server_ProtocolHandler *client)
|
||||
{
|
||||
usersLock.lockForWrite();
|
||||
users.remove(QString::fromStdString(client->getUserInfo()->name()));
|
||||
|
||||
ServerInfo_Room roomInfo;
|
||||
roomInfo.set_room_id(id);
|
||||
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||
usersLock.unlock();
|
||||
|
||||
Event_LeaveRoom event;
|
||||
event.set_name(client->getUserInfo()->name());
|
||||
sendRoomEvent(prepareRoomEvent(event));
|
||||
|
||||
// XXX This can be removed during the next client update.
|
||||
gamesLock.lockForRead();
|
||||
roomInfo.set_game_count(games.size() + externalGames.size());
|
||||
gamesLock.unlock();
|
||||
// -----------
|
||||
|
||||
emit roomInfoChanged(roomInfo);
|
||||
}
|
||||
|
||||
void Server_Room::addExternalUser(const ServerInfo_User &userInfo)
|
||||
{
|
||||
// This function is always called from the Server thread with server->roomsMutex locked.
|
||||
ServerInfo_User_Container userInfoContainer(userInfo);
|
||||
Event_JoinRoom event;
|
||||
event.mutable_user_info()->CopyFrom(userInfoContainer.copyUserInfo(false));
|
||||
sendRoomEvent(prepareRoomEvent(event), false);
|
||||
|
||||
ServerInfo_Room roomInfo;
|
||||
roomInfo.set_room_id(id);
|
||||
|
||||
usersLock.lockForWrite();
|
||||
externalUsers.insert(QString::fromStdString(userInfo.name()), userInfoContainer);
|
||||
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||
usersLock.unlock();
|
||||
|
||||
emit roomInfoChanged(roomInfo);
|
||||
}
|
||||
|
||||
void Server_Room::removeExternalUser(const QString &_name)
|
||||
{
|
||||
// This function is always called from the Server thread with server->roomsMutex locked.
|
||||
ServerInfo_Room roomInfo;
|
||||
roomInfo.set_room_id(id);
|
||||
|
||||
usersLock.lockForWrite();
|
||||
if (externalUsers.contains(_name))
|
||||
externalUsers.remove(_name);
|
||||
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||
usersLock.unlock();
|
||||
|
||||
Event_LeaveRoom event;
|
||||
event.set_name(_name.toStdString());
|
||||
sendRoomEvent(prepareRoomEvent(event), false);
|
||||
|
||||
emit roomInfoChanged(roomInfo);
|
||||
}
|
||||
|
||||
void Server_Room::updateExternalGameList(const ServerInfo_Game &gameInfo)
|
||||
{
|
||||
// This function is always called from the Server thread with server->roomsMutex locked.
|
||||
ServerInfo_Room roomInfo;
|
||||
roomInfo.set_room_id(id);
|
||||
|
||||
gamesLock.lockForWrite();
|
||||
if (!gameInfo.has_player_count() && externalGames.contains(gameInfo.game_id()))
|
||||
externalGames.remove(gameInfo.game_id());
|
||||
else
|
||||
externalGames.insert(gameInfo.game_id(), gameInfo);
|
||||
roomInfo.set_game_count(games.size() + externalGames.size());
|
||||
gamesLock.unlock();
|
||||
|
||||
broadcastGameListUpdate(gameInfo, false);
|
||||
emit roomInfoChanged(roomInfo);
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGame &cmd,
|
||||
ResponseContainer &rc,
|
||||
Server_AbstractUserInterface *userInterface)
|
||||
{
|
||||
if (cmd.password().length() > MAX_NAME_LENGTH)
|
||||
return Response::RespWrongPassword;
|
||||
// This function is called from the Server thread and from the S_PH thread.
|
||||
// server->roomsMutex is always locked.
|
||||
|
||||
QReadLocker roomGamesLocker(&gamesLock);
|
||||
Server_Game *game = games.value(cmd.game_id());
|
||||
if (!game) {
|
||||
if (externalGames.contains(cmd.game_id())) {
|
||||
CommandContainer cont;
|
||||
cont.set_cmd_id(rc.getCmdId());
|
||||
RoomCommand *roomCommand = cont.add_room_command();
|
||||
roomCommand->GetReflection()
|
||||
->MutableMessage(roomCommand, cmd.GetDescriptor()->FindExtensionByName("ext"))
|
||||
->CopyFrom(cmd);
|
||||
getServer()->sendIsl_RoomCommand(cont, externalGames.value(cmd.game_id()).server_id(),
|
||||
userInterface->getUserInfo()->session_id(), id);
|
||||
|
||||
return Response::RespNothing;
|
||||
} else {
|
||||
return Response::RespNameNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
QMutexLocker gameLocker(&game->gameMutex);
|
||||
|
||||
Response::ResponseCode result =
|
||||
game->checkJoin(userInterface->getUserInfo(), QString::fromStdString(cmd.password()), cmd.spectator(),
|
||||
cmd.override_restrictions(), cmd.join_as_judge());
|
||||
if (result == Response::RespOk)
|
||||
game->addPlayer(userInterface, rc, cmd.spectator(), cmd.join_as_judge());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Server_Room::say(const QString &userName, const QString &userMessage, bool sendToIsl)
|
||||
{
|
||||
Event_RoomSay event;
|
||||
event.set_name(userName.toStdString());
|
||||
event.set_message(userMessage.toStdString());
|
||||
sendRoomEvent(prepareRoomEvent(event), sendToIsl);
|
||||
|
||||
if (chatHistorySize != 0) {
|
||||
ServerInfo_ChatMessage chatMessage;
|
||||
QDateTime dateTime = dateTime.currentDateTimeUtc();
|
||||
QString dateTimeString = dateTime.toString();
|
||||
chatMessage.set_time(dateTimeString.toStdString());
|
||||
chatMessage.set_sender_name(userName.toStdString());
|
||||
chatMessage.set_message(userMessage.simplified().toStdString());
|
||||
|
||||
historyLock.lockForWrite();
|
||||
if (chatHistory.size() >= chatHistorySize) {
|
||||
chatHistory.removeAt(0);
|
||||
}
|
||||
|
||||
chatHistory.push_back(std::move(chatMessage));
|
||||
historyLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void Server_Room::removeSaidMessages(const QString &userName, int amount, bool sendToIsl)
|
||||
{
|
||||
Event_RemoveMessages event;
|
||||
auto stdStringUserName = userName.toStdString();
|
||||
event.set_name(stdStringUserName);
|
||||
event.set_amount(amount);
|
||||
sendRoomEvent(prepareRoomEvent(event), sendToIsl);
|
||||
|
||||
if (chatHistorySize != 0) {
|
||||
int removed = 0;
|
||||
historyLock.lockForWrite();
|
||||
// redact [amount] of the most recent messages from this user from history
|
||||
for (auto message = chatHistory.rbegin(); message != chatHistory.rend() && removed != amount; ++message) {
|
||||
if (message->sender_name() == stdStringUserName) {
|
||||
message->clear_message();
|
||||
++removed;
|
||||
}
|
||||
}
|
||||
historyLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void Server_Room::sendRoomEvent(RoomEvent *event, bool sendToIsl)
|
||||
{
|
||||
usersLock.lockForRead();
|
||||
{
|
||||
QMapIterator<QString, Server_ProtocolHandler *> userIterator(users);
|
||||
while (userIterator.hasNext())
|
||||
userIterator.next().value()->sendProtocolItem(*event);
|
||||
}
|
||||
usersLock.unlock();
|
||||
|
||||
if (sendToIsl)
|
||||
static_cast<Server *>(parent())->sendIsl_RoomEvent(*event);
|
||||
|
||||
delete event;
|
||||
}
|
||||
|
||||
void Server_Room::broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool sendToIsl)
|
||||
{
|
||||
Event_ListGames event;
|
||||
event.add_game_list()->CopyFrom(gameInfo);
|
||||
sendRoomEvent(prepareRoomEvent(event), sendToIsl);
|
||||
}
|
||||
|
||||
void Server_Room::addGame(Server_Game *game)
|
||||
{
|
||||
ServerInfo_Room roomInfo;
|
||||
roomInfo.set_room_id(id);
|
||||
|
||||
gamesLock.lockForWrite();
|
||||
connect(game, &Server_Game::gameInfoChanged, this, [this](auto gameInfo) { broadcastGameListUpdate(gameInfo); });
|
||||
|
||||
game->gameMutex.lock();
|
||||
games.insert(game->getGameId(), game);
|
||||
ServerInfo_Game gameInfo;
|
||||
game->getInfo(gameInfo);
|
||||
roomInfo.set_game_count(games.size() + externalGames.size());
|
||||
game->gameMutex.unlock();
|
||||
gamesLock.unlock();
|
||||
|
||||
// XXX This can be removed during the next client update.
|
||||
usersLock.lockForRead();
|
||||
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||
usersLock.unlock();
|
||||
// -----------
|
||||
|
||||
emit gameListChanged(gameInfo);
|
||||
emit roomInfoChanged(roomInfo);
|
||||
}
|
||||
|
||||
void Server_Room::removeGame(Server_Game *game)
|
||||
{
|
||||
// No need to lock gamesLock or gameMutex. This method is only
|
||||
// called from ~Server_Game, which locks both mutexes anyway beforehand.
|
||||
|
||||
disconnect(game, 0, this, 0);
|
||||
|
||||
ServerInfo_Game gameInfo;
|
||||
game->getInfo(gameInfo);
|
||||
emit gameListChanged(gameInfo);
|
||||
|
||||
games.remove(game->getGameId());
|
||||
|
||||
ServerInfo_Room roomInfo;
|
||||
roomInfo.set_room_id(id);
|
||||
roomInfo.set_game_count(games.size() + externalGames.size());
|
||||
|
||||
// XXX This can be removed during the next client update.
|
||||
usersLock.lockForRead();
|
||||
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||
usersLock.unlock();
|
||||
// -----------
|
||||
|
||||
emit roomInfoChanged(roomInfo);
|
||||
}
|
||||
|
||||
int Server_Room::getGamesCreatedByUser(const QString &userName) const
|
||||
{
|
||||
QReadLocker locker(&gamesLock);
|
||||
|
||||
QMapIterator<int, Server_Game *> gamesIterator(games);
|
||||
int result = 0;
|
||||
while (gamesIterator.hasNext())
|
||||
if (gamesIterator.next().value()->getCreatorInfo()->name() == userName.toStdString())
|
||||
++result;
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<ServerInfo_Game> Server_Room::getGamesOfUser(const QString &userName) const
|
||||
{
|
||||
QReadLocker locker(&gamesLock);
|
||||
|
||||
QList<ServerInfo_Game> result;
|
||||
QMapIterator<int, Server_Game *> gamesIterator(games);
|
||||
while (gamesIterator.hasNext()) {
|
||||
Server_Game *game = gamesIterator.next().value();
|
||||
if (game->containsUser(userName)) {
|
||||
ServerInfo_Game gameInfo;
|
||||
game->getInfo(gameInfo);
|
||||
result.append(gameInfo);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
144
common/server/server_room.h
Normal file
144
common/server/server_room.h
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
#ifndef SERVER_ROOM_H
|
||||
#define SERVER_ROOM_H
|
||||
|
||||
#include "../serverinfo_user_container.h"
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/serverinfo_chat_message.pb.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QReadWriteLock>
|
||||
#include <QStringList>
|
||||
|
||||
class Server_DatabaseInterface;
|
||||
class Server_ProtocolHandler;
|
||||
class RoomEvent;
|
||||
class ServerInfo_User;
|
||||
class ServerInfo_Room;
|
||||
class ServerInfo_Game;
|
||||
class Server_Game;
|
||||
class Server;
|
||||
|
||||
class Command_JoinGame;
|
||||
class ResponseContainer;
|
||||
class Server_AbstractUserInterface;
|
||||
|
||||
class Server_Room : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void roomInfoChanged(const ServerInfo_Room &roomInfo);
|
||||
void gameListChanged(const ServerInfo_Game &gameInfo);
|
||||
|
||||
private:
|
||||
int id;
|
||||
int chatHistorySize;
|
||||
QString name;
|
||||
QString description;
|
||||
QString permissionLevel;
|
||||
QString privilegeLevel;
|
||||
bool autoJoin;
|
||||
QString joinMessage;
|
||||
QStringList gameTypes;
|
||||
QMap<int, Server_Game *> games;
|
||||
QMap<int, ServerInfo_Game> externalGames;
|
||||
QMap<QString, Server_ProtocolHandler *> users;
|
||||
QMap<QString, ServerInfo_User_Container> externalUsers;
|
||||
QList<ServerInfo_ChatMessage> chatHistory;
|
||||
private slots:
|
||||
void broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool sendToIsl = true);
|
||||
|
||||
public:
|
||||
mutable QReadWriteLock usersLock;
|
||||
mutable QReadWriteLock gamesLock;
|
||||
mutable QReadWriteLock historyLock;
|
||||
Server_Room(int _id,
|
||||
int _chatHistorySize,
|
||||
const QString &_name,
|
||||
const QString &_description,
|
||||
const QString &_permissionLevel,
|
||||
const QString &_privilegeLevel,
|
||||
bool _autoJoin,
|
||||
const QString &_joinMessage,
|
||||
const QStringList &_gameTypes,
|
||||
Server *parent);
|
||||
~Server_Room() override;
|
||||
int getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
QString getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
QString getDescription() const
|
||||
{
|
||||
return description;
|
||||
}
|
||||
QString getRoomPermission() const
|
||||
{
|
||||
return permissionLevel;
|
||||
}
|
||||
QString getRoomPrivilege() const
|
||||
{
|
||||
return privilegeLevel;
|
||||
}
|
||||
bool getAutoJoin() const
|
||||
{
|
||||
return autoJoin;
|
||||
}
|
||||
bool userMayJoin(const ServerInfo_User &userInfo);
|
||||
QString getJoinMessage() const
|
||||
{
|
||||
return joinMessage;
|
||||
}
|
||||
const QStringList &getGameTypes() const
|
||||
{
|
||||
return gameTypes;
|
||||
}
|
||||
const QMap<int, Server_Game *> &getGames() const
|
||||
{
|
||||
return games;
|
||||
}
|
||||
const QMap<int, ServerInfo_Game> &getExternalGames() const
|
||||
{
|
||||
return externalGames;
|
||||
}
|
||||
Server *getServer() const;
|
||||
const ServerInfo_Room &
|
||||
getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes = false, bool includeExternalData = true) const;
|
||||
int getGamesCreatedByUser(const QString &name) const;
|
||||
QList<ServerInfo_Game> getGamesOfUser(const QString &name) const;
|
||||
QList<ServerInfo_ChatMessage> &getChatHistory()
|
||||
{
|
||||
return chatHistory;
|
||||
}
|
||||
|
||||
void addClient(Server_ProtocolHandler *client);
|
||||
void removeClient(Server_ProtocolHandler *client);
|
||||
|
||||
void addExternalUser(const ServerInfo_User &userInfo);
|
||||
void removeExternalUser(const QString &_name);
|
||||
const QMap<QString, ServerInfo_User_Container> &getExternalUsers() const
|
||||
{
|
||||
return externalUsers;
|
||||
}
|
||||
void updateExternalGameList(const ServerInfo_Game &gameInfo);
|
||||
|
||||
Response::ResponseCode processJoinGameCommand(const Command_JoinGame &cmd,
|
||||
ResponseContainer &rc,
|
||||
Server_AbstractUserInterface *userInterface);
|
||||
|
||||
void say(const QString &userName, const QString &s, bool sendToIsl = true);
|
||||
void removeSaidMessages(const QString &userName, int amount, bool sendToIsl = true);
|
||||
|
||||
void addGame(Server_Game *game);
|
||||
void removeGame(Server_Game *game);
|
||||
|
||||
void sendRoomEvent(RoomEvent *event, bool sendToIsl = true);
|
||||
RoomEvent *prepareRoomEvent(const ::google::protobuf::Message &roomEvent);
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue