diff --git a/cockatrice/cockatrice.pro b/cockatrice/cockatrice.pro index 6b6aff36..a2516aaa 100644 --- a/cockatrice/cockatrice.pro +++ b/cockatrice/cockatrice.pro @@ -52,6 +52,7 @@ HEADERS += src/counter.h \ src/tab.h \ src/tab_server.h \ src/tab_chatchannel.h \ + src/tab_message.h \ src/tab_game.h \ src/tab_deck_storage.h \ src/tab_supervisor.h \ @@ -125,6 +126,7 @@ SOURCES += src/counter.cpp \ src/arrowtarget.cpp \ src/tab_server.cpp \ src/tab_chatchannel.cpp \ + src/tab_message.cpp \ src/tab_game.cpp \ src/tab_deck_storage.cpp \ src/tab_supervisor.cpp \ diff --git a/cockatrice/src/abstractclient.cpp b/cockatrice/src/abstractclient.cpp index d5eaa9c0..924c9617 100644 --- a/cockatrice/src/abstractclient.cpp +++ b/cockatrice/src/abstractclient.cpp @@ -33,9 +33,12 @@ void AbstractClient::processProtocolItem(ProtocolItem *item) if (genericEvent) { switch (genericEvent->getItemId()) { case ItemId_Event_ListGames: emit listGamesEventReceived(qobject_cast(item)); break; + case ItemId_Event_UserJoined: emit userJoinedEventReceived(qobject_cast(item)); break; + case ItemId_Event_UserLeft: emit userLeftEventReceived(qobject_cast(item)); break; case ItemId_Event_ServerMessage: emit serverMessageEventReceived(qobject_cast(item)); break; case ItemId_Event_ListChatChannels: emit listChatChannelsEventReceived(qobject_cast(item)); break; case ItemId_Event_GameJoined: emit gameJoinedEventReceived(qobject_cast(item)); break; + case ItemId_Event_Message: emit messageEventReceived(qobject_cast(item)); break; } if (genericEvent->getReceiverMayDelete()) delete genericEvent; diff --git a/cockatrice/src/abstractclient.h b/cockatrice/src/abstractclient.h index 47f378b6..601e55db 100644 --- a/cockatrice/src/abstractclient.h +++ b/cockatrice/src/abstractclient.h @@ -13,9 +13,12 @@ class CommandContainer; class ChatEvent; class GameEventContainer; class Event_ListGames; +class Event_UserJoined; +class Event_UserLeft; class Event_ServerMessage; class Event_ListChatChannels; class Event_GameJoined; +class Event_Message; enum ClientStatus { StatusDisconnected, @@ -38,9 +41,12 @@ signals: void gameEventContainerReceived(GameEventContainer *event); // Generic events void listGamesEventReceived(Event_ListGames *event); + void userJoinedEventReceived(Event_UserJoined *event); + void userLeftEventReceived(Event_UserLeft *event); void serverMessageEventReceived(Event_ServerMessage *event); void listChatChannelsEventReceived(Event_ListChatChannels *event); void gameJoinedEventReceived(Event_GameJoined *event); + void messageEventReceived(Event_Message *event); protected slots: void processProtocolItem(ProtocolItem *item); protected: diff --git a/cockatrice/src/pixmapgenerator.cpp b/cockatrice/src/pixmapgenerator.cpp index 70fffb23..e3cfe87d 100644 --- a/cockatrice/src/pixmapgenerator.cpp +++ b/cockatrice/src/pixmapgenerator.cpp @@ -2,6 +2,7 @@ #include #include #include +#include QPixmap PingPixmapGenerator::generatePixmap(int size, int value, int max) { @@ -39,11 +40,13 @@ QPixmap CountryPixmapGenerator::generatePixmap(int height, const QString &countr return pmCache.value(key); QSvgRenderer svg(QString(":/resources/countries/" + countryCode + ".svg")); - double aspect = (double) svg.defaultSize().width() / (double) svg.defaultSize().height(); - QPixmap pixmap((int) round(aspect * height), height); + int width = (int) round(height * (double) svg.defaultSize().width() / (double) svg.defaultSize().height()); + QPixmap pixmap(width, height); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); - svg.render(&painter, QRectF(0, 0, aspect * height, height)); + svg.render(&painter, QRectF(0, 0, width, height)); + painter.setPen(Qt::black); + painter.drawRect(0.5, 0.5, width - 1, height - 1); pmCache.insert(key, pixmap); return pixmap; diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 545dbd63..8eee6880 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -466,7 +466,7 @@ void Player::actMoveTopCardsToGrave() void Player::actMoveTopCardsToExile() { - int number = QInputDialog::getInteger(0, tr("Move top cards to grave"), tr("Number:")); + int number = QInputDialog::getInteger(0, tr("Move top cards to exile"), tr("Number:")); if (!number) return; diff --git a/cockatrice/src/playerlistwidget.cpp b/cockatrice/src/playerlistwidget.cpp index 8fcbce65..f634f058 100644 --- a/cockatrice/src/playerlistwidget.cpp +++ b/cockatrice/src/playerlistwidget.cpp @@ -13,6 +13,7 @@ PlayerListWidget::PlayerListWidget(QWidget *parent) playerIcon = QIcon(":/resources/icon_player.svg"); spectatorIcon = QIcon(":/resources/icon_spectator.svg"); + setIconSize(QSize(20, 15)); setColumnCount(5); setRootIsDecorated(false); setSelectionMode(NoSelection); @@ -47,7 +48,7 @@ void PlayerListWidget::updatePlayerProperties(ServerInfo_PlayerProperties *prop) player->setIcon(2, gameStarted ? (prop->getConceded() ? concededIcon : QIcon()) : (prop->getReadyStart() ? readyIcon : notReadyIcon)); player->setText(3, prop->getUserInfo()->getName()); if (!prop->getUserInfo()->getCountry().isEmpty()) - player->setIcon(3, QIcon(CountryPixmapGenerator::generatePixmap(10, prop->getUserInfo()->getCountry()))); + player->setIcon(3, QIcon(CountryPixmapGenerator::generatePixmap(12, prop->getUserInfo()->getCountry()))); QString deckText; if (!prop->getSpectator()) diff --git a/cockatrice/src/tab_chatchannel.cpp b/cockatrice/src/tab_chatchannel.cpp index 979618c8..515844f1 100644 --- a/cockatrice/src/tab_chatchannel.cpp +++ b/cockatrice/src/tab_chatchannel.cpp @@ -49,6 +49,14 @@ void TabChatChannel::retranslateUi() aLeaveChannel->setText(tr("&Leave channel")); } +QString TabChatChannel::sanitizeHtml(QString dirty) const +{ + return dirty + .replace("&", "&") + .replace("<", "<") + .replace(">", ">"); +} + void TabChatChannel::sendMessage() { if (sayEdit->text().isEmpty()) @@ -84,14 +92,14 @@ void TabChatChannel::processListPlayersEvent(Event_ChatListPlayers *event) void TabChatChannel::processJoinChannelEvent(Event_ChatJoinChannel *event) { - textEdit->append(tr("%1 has joined the channel.").arg(event->getUserInfo()->getName())); + textEdit->append(tr("%1 has joined the channel.").arg(sanitizeHtml(event->getUserInfo()->getName()))); playerList->addItem(event->getUserInfo()->getName()); emit userEvent(); } void TabChatChannel::processLeaveChannelEvent(Event_ChatLeaveChannel *event) { - textEdit->append(tr("%1 has left the channel.").arg(event->getPlayerName())); + textEdit->append(tr("%1 has left the channel.").arg(sanitizeHtml(event->getPlayerName()))); for (int i = 0; i < playerList->count(); ++i) if (playerList->item(i)->text() == event->getPlayerName()) { delete playerList->takeItem(i); @@ -103,8 +111,8 @@ void TabChatChannel::processLeaveChannelEvent(Event_ChatLeaveChannel *event) void TabChatChannel::processSayEvent(Event_ChatSay *event) { if (event->getPlayerName().isEmpty()) - textEdit->append(QString("%1getMessage())); + textEdit->append(QString("%1getMessage()))); else - textEdit->append(QString("%1: %2").arg(event->getPlayerName()).arg(event->getMessage())); + textEdit->append(QString("%1: %2").arg(sanitizeHtml(event->getPlayerName())).arg(sanitizeHtml(event->getMessage()))); emit userEvent(); } diff --git a/cockatrice/src/tab_chatchannel.h b/cockatrice/src/tab_chatchannel.h index 812c689d..2c329199 100644 --- a/cockatrice/src/tab_chatchannel.h +++ b/cockatrice/src/tab_chatchannel.h @@ -24,6 +24,7 @@ private: QLineEdit *sayEdit; QAction *aLeaveChannel; + QString sanitizeHtml(QString dirty) const; signals: void channelClosing(TabChatChannel *tab); private slots: diff --git a/cockatrice/src/tab_server.cpp b/cockatrice/src/tab_server.cpp index 78f2050b..5439f8af 100644 --- a/cockatrice/src/tab_server.cpp +++ b/cockatrice/src/tab_server.cpp @@ -15,6 +15,8 @@ #include "abstractclient.h" #include "protocol.h" #include "protocol_items.h" +#include "pixmapgenerator.h" +#include GameSelector::GameSelector(AbstractClient *_client, QWidget *parent) : QGroupBox(parent), client(_client) @@ -235,23 +237,116 @@ void ServerMessageLog::processServerMessageEvent(Event_ServerMessage *event) textEdit->append(event->getMessage()); } +UserList::UserList(AbstractClient *_client, QWidget *parent) + : QGroupBox(parent) +{ + userTree = new QTreeWidget; + userTree->setColumnCount(2); + userTree->header()->setResizeMode(QHeaderView::ResizeToContents); + userTree->setHeaderHidden(true); + userTree->setRootIsDecorated(false); + userTree->setIconSize(QSize(20, 12)); + connect(userTree, SIGNAL(itemActivated(QTreeWidgetItem *, int)), this, SLOT(userClicked(QTreeWidgetItem *, int))); + + QVBoxLayout *vbox = new QVBoxLayout; + vbox->addWidget(userTree); + + setLayout(vbox); + + retranslateUi(); + + connect(_client, SIGNAL(userJoinedEventReceived(Event_UserJoined *)), this, SLOT(processUserJoinedEvent(Event_UserJoined *))); + connect(_client, SIGNAL(userLeftEventReceived(Event_UserLeft *)), this, SLOT(processUserLeftEvent(Event_UserLeft *))); + + Command_ListUsers *cmd = new Command_ListUsers; + connect(cmd, SIGNAL(finished(ProtocolResponse *)), this, SLOT(processResponse(ProtocolResponse *))); + _client->sendCommand(cmd); +} + +void UserList::retranslateUi() +{ + setTitle(tr("Users online: %1").arg(userTree->topLevelItemCount())); +} + +void UserList::processUserInfo(ServerInfo_User *user) +{ + QTreeWidgetItem *item = 0; + for (int i = 0; i < userTree->topLevelItemCount(); ++i) { + QTreeWidgetItem *temp = userTree->topLevelItem(i); + if (temp->data(1, Qt::UserRole) == user->getName()) { + item = temp; + break; + } + } + if (!item) { + item = new QTreeWidgetItem; + userTree->addTopLevelItem(item); + retranslateUi(); + } + item->setIcon(0, QIcon(CountryPixmapGenerator::generatePixmap(12, user->getCountry()))); + item->setData(1, Qt::UserRole, user->getName()); + item->setData(1, Qt::DisplayRole, user->getName()); +} + +void UserList::processResponse(ProtocolResponse *response) +{ + Response_ListUsers *resp = qobject_cast(response); + if (!resp) + return; + + const QList &respList = resp->getUserList(); + for (int i = 0; i < respList.size(); ++i) + processUserInfo(respList[i]); + + userTree->sortItems(1, Qt::AscendingOrder); +} + +void UserList::processUserJoinedEvent(Event_UserJoined *event) +{ + processUserInfo(event->getUserInfo()); + userTree->sortItems(1, Qt::AscendingOrder); +} + +void UserList::processUserLeftEvent(Event_UserLeft *event) +{ + for (int i = 0; i < userTree->topLevelItemCount(); ++i) + if (userTree->topLevelItem(i)->data(1, Qt::UserRole) == event->getUserName()) { + emit userLeft(event->getUserName()); + delete userTree->takeTopLevelItem(i); + retranslateUi(); + break; + } +} + +void UserList::userClicked(QTreeWidgetItem *item, int /*column*/) +{ + emit openMessageDialog(item->data(1, Qt::UserRole).toString(), true); +} + TabServer::TabServer(AbstractClient *_client, QWidget *parent) : Tab(parent), client(_client) { gameSelector = new GameSelector(client); chatChannelSelector = new ChatChannelSelector(client); serverMessageLog = new ServerMessageLog(client); + userList = new UserList(client); connect(gameSelector, SIGNAL(gameJoined(int)), this, SIGNAL(gameJoined(int))); connect(chatChannelSelector, SIGNAL(channelJoined(const QString &)), this, SIGNAL(chatChannelJoined(const QString &))); + connect(userList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool))); + connect(userList, SIGNAL(userLeft(const QString &)), this, SIGNAL(userLeft(const QString &))); QHBoxLayout *hbox = new QHBoxLayout; hbox->addWidget(chatChannelSelector); hbox->addWidget(serverMessageLog); - QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->addWidget(gameSelector); - mainLayout->addLayout(hbox); + QVBoxLayout *vbox = new QVBoxLayout; + vbox->addWidget(gameSelector); + vbox->addLayout(hbox); + + QHBoxLayout *mainLayout = new QHBoxLayout; + mainLayout->addLayout(vbox, 3); + mainLayout->addWidget(userList, 1); setLayout(mainLayout); } @@ -261,4 +356,5 @@ void TabServer::retranslateUi() gameSelector->retranslateUi(); chatChannelSelector->retranslateUi(); serverMessageLog->retranslateUi(); + userList->retranslateUi(); } diff --git a/cockatrice/src/tab_server.h b/cockatrice/src/tab_server.h index 08771582..643bae2a 100644 --- a/cockatrice/src/tab_server.h +++ b/cockatrice/src/tab_server.h @@ -8,6 +8,7 @@ class AbstractClient; class QTreeView; class QTreeWidget; +class QTreeWidgetItem; class QPushButton; class QCheckBox; class QTextEdit; @@ -18,6 +19,9 @@ class GamesProxyModel; class Event_ListGames; class Event_ListChatChannels; class Event_ServerMessage; +class Event_UserJoined; +class Event_UserLeft; +class ProtocolResponse; class GameSelector : public QGroupBox { Q_OBJECT @@ -72,16 +76,37 @@ public: void retranslateUi(); }; +class UserList : public QGroupBox { + Q_OBJECT +private: + QTreeWidget *userTree; + void processUserInfo(ServerInfo_User *user); +private slots: + void processResponse(ProtocolResponse *response); + void processUserJoinedEvent(Event_UserJoined *event); + void processUserLeftEvent(Event_UserLeft *event); + void userClicked(QTreeWidgetItem *item, int column); +signals: + void openMessageDialog(const QString &userName, bool focus); + void userLeft(const QString &userName); +public: + UserList(AbstractClient *_client, QWidget *parent = 0); + void retranslateUi(); +}; + class TabServer : public Tab { Q_OBJECT signals: void chatChannelJoined(const QString &channelName); void gameJoined(int gameId); + void openMessageDialog(const QString &userName, bool focus); + void userLeft(const QString &userName); private: AbstractClient *client; GameSelector *gameSelector; ChatChannelSelector *chatChannelSelector; ServerMessageLog *serverMessageLog; + UserList *userList; public: TabServer(AbstractClient *_client, QWidget *parent = 0); void retranslateUi(); diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index 84d6446d..b5075470 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -5,6 +5,7 @@ #include "tab_chatchannel.h" #include "tab_game.h" #include "tab_deck_storage.h" +#include "tab_message.h" #include "protocol_items.h" #include "pixmapgenerator.h" #include @@ -56,10 +57,13 @@ void TabSupervisor::start(AbstractClient *_client) connect(client, SIGNAL(chatEventReceived(ChatEvent *)), this, SLOT(processChatEvent(ChatEvent *))); connect(client, SIGNAL(gameEventContainerReceived(GameEventContainer *)), this, SLOT(processGameEventContainer(GameEventContainer *))); connect(client, SIGNAL(gameJoinedEventReceived(Event_GameJoined *)), this, SLOT(gameJoined(Event_GameJoined *))); + connect(client, SIGNAL(messageEventReceived(Event_Message *)), this, SLOT(processMessageEvent(Event_Message *))); connect(client, SIGNAL(maxPingTime(int, int)), this, SLOT(updatePingTime(int, int))); tabServer = new TabServer(client); connect(tabServer, SIGNAL(chatChannelJoined(const QString &)), this, SLOT(addChatChannelTab(const QString &))); + connect(tabServer, SIGNAL(openMessageDialog(const QString &, bool)), this, SLOT(addMessageTab(const QString &, bool))); + connect(tabServer, SIGNAL(userLeft(const QString &)), this, SLOT(processUserLeft(const QString &))); myAddTab(tabServer); updatePingTime(0, -1); @@ -173,6 +177,25 @@ void TabSupervisor::chatChannelLeft(TabChatChannel *tab) removeTab(indexOf(tab)); } +TabMessage *TabSupervisor::addMessageTab(const QString &userName, bool focus) +{ + TabMessage *tab = new TabMessage(client, userName); + connect(tab, SIGNAL(talkClosing(TabMessage *)), this, SLOT(talkLeft(TabMessage *))); + myAddTab(tab); + messageTabs.insert(userName, tab); + if (focus) + setCurrentWidget(tab); + return tab; +} + +void TabSupervisor::talkLeft(TabMessage *tab) +{ + emit setMenu(0); + + messageTabs.remove(tab->getUserName()); + removeTab(indexOf(tab)); +} + void TabSupervisor::tabUserEvent() { Tab *tab = static_cast(sender()); @@ -200,6 +223,23 @@ void TabSupervisor::processGameEventContainer(GameEventContainer *cont) qDebug() << "gameEvent: invalid gameId"; } +void TabSupervisor::processMessageEvent(Event_Message *event) +{ + TabMessage *tab = messageTabs.value(event->getSenderName()); + if (!tab) + tab = messageTabs.value(event->getReceiverName()); + if (!tab) + tab = addMessageTab(event->getSenderName(), false); + tab->processMessageEvent(event); +} + +void TabSupervisor::processUserLeft(const QString &userName) +{ + TabMessage *tab = messageTabs.value(userName); + if (tab) + tab->processUserLeft(userName); +} + void TabSupervisor::updateCurrent(int index) { if (index != -1) { diff --git a/cockatrice/src/tab_supervisor.h b/cockatrice/src/tab_supervisor.h index 58ad3c19..7d1965f8 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -11,9 +11,11 @@ class TabServer; class TabChatChannel; class TabGame; class TabDeckStorage; +class TabMessage; class ChatEvent; class GameEventContainer; class Event_GameJoined; +class Event_Message; class TabSupervisor : public QTabWidget { Q_OBJECT @@ -25,6 +27,7 @@ private: TabDeckStorage *tabDeckStorage; QMap chatChannelTabs; QMap gameTabs; + QMap messageTabs; void myAddTab(Tab *tab); public: TabSupervisor(QWidget *parent = 0); @@ -45,9 +48,13 @@ private slots: void gameLeft(TabGame *tab); void addChatChannelTab(const QString &channelName); void chatChannelLeft(TabChatChannel *tab); + TabMessage *addMessageTab(const QString &userName, bool focus); + void processUserLeft(const QString &userName); + void talkLeft(TabMessage *tab); void tabUserEvent(); void processChatEvent(ChatEvent *event); void processGameEventContainer(GameEventContainer *cont); + void processMessageEvent(Event_Message *event); }; #endif diff --git a/cockatrice/translations/cockatrice_de.ts b/cockatrice/translations/cockatrice_de.ts index e7ffe2fe..79b570fe 100644 --- a/cockatrice/translations/cockatrice_de.ts +++ b/cockatrice/translations/cockatrice_de.ts @@ -398,27 +398,27 @@ ChatChannelSelector - + Chat channels Chaträume - + Joi&n Teil&nehmen - + Channel Raum - + Description Beschreibung - + Players Spieler @@ -1162,20 +1162,20 @@ GameSelector - + C&reate Spiel e&rstellen - + &Join &Teilnehmen - - + + Error Fehler @@ -1184,47 +1184,47 @@ XXX - + Wrong password. Falsches Passwort. - + Spectators are not allowed in this game. In diesem Spiel sind keine Zuschauer zugelassen. - + The game is already full. Das Spiel ist bereits voll. - + The game does not exist any more. Dieses Spiel gibt es nicht mehr. - + Join game Spiel beitreten - + Password: Passwort: - + Games Spiele - + &Show full games &Volle Spiele anzeigen - + J&oin as spectator &Zuschauen @@ -1810,27 +1810,37 @@ %1 legt %2%3 in sein Sideboard. - + + %1 flips %2 face-down. + %1 wendet %2 auf die Rückseite. + + + + %1 flips %2 face-up. + %1 wendet %2 auf die Vorderseite. + + + %1 destroys %2. %1 zerstört %2. - + %1 attaches %2 to %3's %4. %1 legt %2 an %3s %4 an. - + %1 unattaches %2. %1 löst %2 ab. - + %1 creates token: %2%3. %1 erstellt Token: %2%3. - + %1 points from %2's %3 to %4. %1 zeigt von %2s %3 auf %4. @@ -1839,12 +1849,12 @@ %1 erstellt einen Spielstein: %2 (%3). - + %1 points from %2's %3 to %4's %5. %1 zeigt von %2s %3 auf %4s %5. - + %1 places %n counter(s) (%2) on %3 (now %4). %1 legt eine Marke (%2) auf %3 (jetzt %4). @@ -1852,7 +1862,7 @@ - + %1 removes %n counter(s) (%2) from %3 (now %4). %1 entfernt eine Marke (%2) von %3 (jetzt %4). @@ -1860,37 +1870,37 @@ - + red rot - + yellow gelb - + green grün - + %1 sets counter %2 to %3 (%4%5). %1 setzt Zähler %2 auf %3 (%4%5). - + %1 sets PT of %2 to %3. %1 setzt Kampfwerte von %2 auf %3. - + %1 sets annotation of %2 to %3. %1 versieht %2 mit dem Hinweis %3. - + %1 is looking at the top %2 cards %3. %1 sieht sich die obersten %2 Karten %3 an. @@ -1987,7 +1997,7 @@ %1 entfernt %2 Zählmarken von %3 (jetzt %4). - + %1 %2 %3. %1 %2 %3. @@ -2000,17 +2010,17 @@ %1 sieht sich die obersten %2 Karten %3 an. - + %1 is looking at %2. %1 sieht sich %2 an. - + %1 stops looking at %2. %1 sieht sich %2 nicht mehr an. - + ending phase die Zugendphase @@ -2039,57 +2049,57 @@ %1 sieht sich %2s %3 nicht mehr an - + It is now %1's turn. %1 ist am Zug. - + untap step das Enttappsegment - + upkeep step das Versorgungssegment - + draw step das Ziehsegment - + first main phase die erste Hauptphase - + beginning of combat step das Anfangssegment der Kampfphase - + declare attackers step das Angreifer-Deklarieren-Segment - + declare blockers step das Blocker-Deklarieren-Segment - + combat damage step das Kampfschadenssegment - + end of combat step das Endsegment der Kampfphase - + second main phase die zweite Hauptphase @@ -2098,7 +2108,7 @@ das Ende-des-Zuges-Segment - + It is now the %1. Es ist nun %1. @@ -2107,12 +2117,12 @@ %1 bewegt %2 %3 nach %4 - + taps tappt - + untaps enttappt @@ -2137,7 +2147,7 @@ %1 entfernt %2 Zählmarken von %3 (jetzt %4) - + his permanents seine bleibenden Karten @@ -2150,12 +2160,12 @@ %1 setzt Zähler "%2" auf %3 (%4%5) - + %1 sets %2 to not untap normally. %1 setzt %2 auf explizites Enttappen. - + %1 sets %2 to untap normally. %1 setzt %2 auf normales Enttappen. @@ -2260,41 +2270,51 @@ Player - - - + + + Move to &top of library Oben auf die Biblio&thek legen - - - + + + Move to &bottom of library Unter die &Bibliothek legen - + &View library &Zeige Bibliothek - + + Move top cards to g&raveyard... + Oberste Karten in den F&riedhof legen... + + + + Move top cards to &exile... + Oberste Karten ins &Exil schicken... + + + F3 F3 - + View &top cards of library... Zeige die oberen Kar&ten der Bibliothek... - + &View graveyard &Zeige Friedhof - + F4 F4 @@ -2303,32 +2323,32 @@ Zeige ent&fernte Karten - + &View sideboard Zeige &Sideboard - + Player "%1" Spieler "%1" - + Take &mulligan &Mulligan nehmen - + &Hand &Hand - + &Library Bib&liothek - + &Graveyard &Friedhof @@ -2337,7 +2357,7 @@ Entfe&rnte Karten - + &Sideboard &Sideboard @@ -2350,65 +2370,65 @@ &Hinweis setzen... - + View top cards of library Zeige die obersten Karten der Bibliothek - + Number of cards: Anzahl der Karten: - + &Draw card Karte &ziehen - + &View exile &Zeige Exil - + &Exile &Exil - - + + Move to &hand auf die &Hand nehmen - - + + Move to g&raveyard auf den &Friedhof legen - - + + Move to &exile ins &Exil schicken - + Ctrl+W Ctrl+W - + Ctrl+D Ctrl+D - + D&raw cards... Ka&rten ziehen... - + Ctrl+E Ctrl+E @@ -2417,32 +2437,32 @@ &Mulligan nehmen... - + Ctrl+M Ctrl+M - + &Shuffle Mi&schen - + Ctrl+S Ctrl+S - + &Counters &Zähler - + &Untap all permanents &Enttappe alle bleibenden Karten - + Ctrl+U Ctrl+U @@ -2471,42 +2491,42 @@ Ctrl+L - + R&oll die... &Würfeln... - + Ctrl+I Ctrl+I - + &Create token... Spiels&tein erstellen... - + Ctrl+T Ctrl+T - + C&reate another token &Noch einen Spielstein erstellen - + Ctrl+G Ctrl+G - + S&ay S&agen - + C&ard &Karte @@ -2599,38 +2619,50 @@ F10 - + Draw cards Karten ziehen - - + + + + Number: Anzahl: - + + Move top cards to grave + Oberste Karten in den Friedhof legen + + + + Move top cards to exile + Oberste Karten ins Exil schicken + + + Set power/toughness Kampfwerte setzen - + Please enter the new PT: Bitte die neuen Kampfwerte eingeben: - + Set annotation Hinweis setzen - + Please enter the new annotation: Bitte den Hinweis eingeben: - + Set counters Setze Zählmarken @@ -2643,12 +2675,12 @@ Neue Lebenspunkte insgesamt: - + Roll die Würfeln - + Number of sides: Anzahl der Seiten: @@ -2664,29 +2696,41 @@ PlayerListWidget - + Player name Spielername - + Deck Deck - + + --- + --- + + + + local + lokal + + + + #%1 + #%1 + + no deck - kein Deck + kein Deck - local deck - lokales Deck + lokales Deck - ID #%1 - ID #%1 + ID #%1 Role @@ -2750,7 +2794,7 @@ ServerMessageLog - + Server messages Servernachrichten @@ -2781,12 +2825,12 @@ Raum ver&lassen - + %1 has joined the channel. %1 hat den Raum betreten. - + %1 has left the channel. %1 hat den Raum verlassen. @@ -2975,10 +3019,33 @@ Bitte geben Sie einen Namen ein: Spiel %1: %2 + + TabMessage + + + Personal &talk + Persönliches &Gespräch + + + + &Leave + Ver&lassen + + + + %1 has left the server. + %1 hat den Server verlassen. + + + + Talking to %1 + Gespräch mit %1 + + TabServer - + Server Server @@ -3011,6 +3078,14 @@ Bitte geben Sie einen Namen ein: Karten durch &Doppelklick ausspielen (statt Einzelklick) + + UserList + + + Users online: %1 + Benutzer online: %1 + + WndDeckEditor diff --git a/cockatrice/translations/cockatrice_en.ts b/cockatrice/translations/cockatrice_en.ts index 41cd8323..26f28146 100644 --- a/cockatrice/translations/cockatrice_en.ts +++ b/cockatrice/translations/cockatrice_en.ts @@ -352,27 +352,27 @@ ChatChannelSelector - + Chat channels - + Joi&n - + Channel - + Description - + Players @@ -785,65 +785,65 @@ GameSelector - + C&reate - + &Join - - + + Error - + Wrong password. - + Spectators are not allowed in this game. - + The game is already full. - + The game does not exist any more. - + Join game - + Password: - + Games - + &Show full games - + J&oin as spectator @@ -1220,22 +1220,32 @@ - + + %1 flips %2 face-down. + + + + + %1 flips %2 face-up. + + + + %1 attaches %2 to %3's %4. - + %1 unattaches %2. - + %1 points from %2's %3 to %4's %5. - + %1 places %n counter(s) (%2) on %3 (now %4). %1 places a counter (%2) on %3 (now %4). @@ -1243,7 +1253,7 @@ - + %1 removes %n counter(s) (%2) from %3 (now %4). %1 removes a counter (%2) from %3 (now %4). @@ -1251,37 +1261,37 @@ - + red - + yellow - + green - + %1 sets counter %2 to %3 (%4%5). - + %1 sets PT of %2 to %3. - + %1 sets annotation of %2 to %3. - + %1 is looking at the top %2 cards %3. @@ -1346,42 +1356,42 @@ - + %1 destroys %2. - + %1 creates token: %2%3. - + %1 points from %2's %3 to %4. - + %1 %2 %3. - + %1 is looking at %2. - + %1 stops looking at %2. - + ending phase - + It is now %1's turn. @@ -1391,82 +1401,82 @@ - + untap step - + upkeep step - + draw step - + first main phase - + beginning of combat step - + declare attackers step - + declare blockers step - + combat damage step - + end of combat step - + second main phase - + It is now the %1. - + taps - + untaps - + %1 sets %2 to not untap normally. - + %1 sets %2 to untap normally. - + his permanents @@ -1555,255 +1565,277 @@ Player - - - + + + Move to &top of library - - - + + + Move to &bottom of library - + &View library - + F3 - + View &top cards of library... - + &View graveyard - + F4 - + &View sideboard - + Player "%1" - + &Hand - + &Library - + &Graveyard - + &Sideboard - + View top cards of library - + Number of cards: - + &Draw card - + &View exile - + &Exile - - + + Move to &hand - - + + Move to g&raveyard - - + + Move to &exile - + Ctrl+W - + Ctrl+D - + D&raw cards... - + Ctrl+E - + Take &mulligan - + Ctrl+M - + &Shuffle - + Ctrl+S - + &Counters - + &Untap all permanents - + Ctrl+U - + R&oll die... - + Ctrl+I - + &Create token... - + Ctrl+T - + C&reate another token - + Ctrl+G - + S&ay - + + Move top cards to g&raveyard... + + + + + Move top cards to &exile... + + + + C&ard - + Draw cards - - + + + + Number: - + + Move top cards to grave + + + + + Move top cards to exile + + + + Roll die - + Number of sides: - + Set power/toughness - + Please enter the new PT: - + Set annotation - + Please enter the new annotation: - + Set counters @@ -1811,28 +1843,28 @@ PlayerListWidget - + Player name - + Deck - - no deck + + --- - - local deck + + local - - ID #%1 + + #%1 @@ -1885,7 +1917,7 @@ ServerMessageLog - + Server messages @@ -1916,12 +1948,12 @@ - + %1 has joined the channel. - + %1 has left the channel. @@ -2081,10 +2113,33 @@ Please enter a name: + + TabMessage + + + Personal &talk + + + + + &Leave + + + + + %1 has left the server. + + + + + Talking to %1 + + + TabServer - + Server @@ -2102,6 +2157,14 @@ Please enter a name: + + UserList + + + Users online: %1 + + + WndDeckEditor diff --git a/common/protocol.cpp b/common/protocol.cpp index 7b320242..4facb234 100644 --- a/common/protocol.cpp +++ b/common/protocol.cpp @@ -37,12 +37,14 @@ void ProtocolItem::initializeHash() registerSerializableItem("resp", ProtocolResponse::newItem); ProtocolResponse::initializeHash(); + registerSerializableItem("resplist_users", Response_ListUsers::newItem); registerSerializableItem("respdeck_list", Response_DeckList::newItem); registerSerializableItem("respdeck_download", Response_DeckDownload::newItem); registerSerializableItem("respdeck_upload", Response_DeckUpload::newItem); registerSerializableItem("respdump_zone", Response_DumpZone::newItem); registerSerializableItem("generic_eventlist_games", Event_ListGames::newItem); + registerSerializableItem("generic_eventuser_joined", Event_UserJoined::newItem); registerSerializableItem("generic_eventlist_chat_channels", Event_ListChatChannels::newItem); registerSerializableItem("game_eventjoin", Event_Join::newItem); registerSerializableItem("game_eventgame_state_changed", Event_GameStateChanged::newItem); @@ -215,6 +217,13 @@ void ProtocolResponse::initializeHash() responseHash.insert("spectators_not_allowed", RespSpectatorsNotAllowed); } +Response_ListUsers::Response_ListUsers(int _cmdId, ResponseCode _responseCode, const QList &_userList) + : ProtocolResponse(_cmdId, _responseCode, "list_users") +{ + for (int i = 0; i < _userList.size(); ++i) + itemList.append(_userList[i]); +} + Response_DeckList::Response_DeckList(int _cmdId, ResponseCode _responseCode, DeckList_Directory *_root) : ProtocolResponse(_cmdId, _responseCode, "deck_list") { @@ -298,6 +307,14 @@ Event_ListGames::Event_ListGames(const QList &_gameList) itemList.append(_gameList[i]); } +Event_UserJoined::Event_UserJoined(ServerInfo_User *_userInfo) + : GenericEvent("user_joined") +{ + if (!_userInfo) + _userInfo = new ServerInfo_User; + insertItem(_userInfo); +} + Event_Join::Event_Join(ServerInfo_PlayerProperties *player) : GameEvent("join", -1) { diff --git a/common/protocol.h b/common/protocol.h index e6599069..32fbf0ee 100644 --- a/common/protocol.h +++ b/common/protocol.h @@ -29,17 +29,19 @@ enum ItemId { ItemId_Event_ChatListPlayers = ItemId_Other + 201, ItemId_Event_ChatJoinChannel = ItemId_Other + 202, ItemId_Event_ListGames = ItemId_Other + 203, - ItemId_Event_GameStateChanged = ItemId_Other + 204, - ItemId_Event_PlayerPropertiesChanged = ItemId_Other + 205, - ItemId_Event_CreateArrows = ItemId_Other + 206, - ItemId_Event_CreateCounters = ItemId_Other + 207, - ItemId_Event_DrawCards = ItemId_Other + 208, - ItemId_Event_Join = ItemId_Other + 209, - ItemId_Event_Ping = ItemId_Other + 210, - ItemId_Response_DeckList = ItemId_Other + 300, - ItemId_Response_DeckDownload = ItemId_Other + 301, - ItemId_Response_DeckUpload = ItemId_Other + 302, - ItemId_Response_DumpZone = ItemId_Other + 303, + ItemId_Event_UserJoined = ItemId_Other + 204, + ItemId_Event_GameStateChanged = ItemId_Other + 205, + ItemId_Event_PlayerPropertiesChanged = ItemId_Other + 206, + ItemId_Event_CreateArrows = ItemId_Other + 207, + ItemId_Event_CreateCounters = ItemId_Other + 208, + ItemId_Event_DrawCards = ItemId_Other + 209, + ItemId_Event_Join = ItemId_Other + 210, + ItemId_Event_Ping = ItemId_Other + 211, + ItemId_Response_ListUsers = ItemId_Other + 300, + ItemId_Response_DeckList = ItemId_Other + 301, + ItemId_Response_DeckDownload = ItemId_Other + 302, + ItemId_Response_DeckUpload = ItemId_Other + 303, + ItemId_Response_DumpZone = ItemId_Other + 304, ItemId_Invalid = ItemId_Other + 1000 }; @@ -199,6 +201,15 @@ public: ResponseCode getResponseCode() const { return responseHash.value(static_cast(itemMap.value("response_code"))->getData(), RespOk); } }; +class Response_ListUsers : public ProtocolResponse { + Q_OBJECT +public: + Response_ListUsers(int _cmdId = -1, ResponseCode _responseCode = RespOk, const QList &_userList = QList()); + int getItemId() const { return ItemId_Response_ListUsers; } + static SerializableItem *newItem() { return new Response_ListUsers; } + QList getUserList() const { return typecastItemList(); } +}; + class Response_DeckList : public ProtocolResponse { Q_OBJECT public: @@ -323,6 +334,15 @@ public: QList getGameList() const { return typecastItemList(); } }; +class Event_UserJoined : public GenericEvent { + Q_OBJECT +public: + Event_UserJoined(ServerInfo_User *_userInfo = 0); + int getItemId() const { return ItemId_Event_UserJoined; } + static SerializableItem *newItem() { return new Event_UserJoined; } + ServerInfo_User *getUserInfo() const { return static_cast(itemMap.value("user")); } +}; + class Event_Join : public GameEvent { Q_OBJECT public: diff --git a/common/protocol_item_ids.h b/common/protocol_item_ids.h index c9bc9b9d..211e03ac 100644 --- a/common/protocol_item_ids.h +++ b/common/protocol_item_ids.h @@ -1,68 +1,72 @@ enum AutoItemId { ItemId_Command_Ping = 1001, ItemId_Command_Login = 1002, -ItemId_Command_DeckList = 1003, -ItemId_Command_DeckNewDir = 1004, -ItemId_Command_DeckDelDir = 1005, -ItemId_Command_DeckDel = 1006, -ItemId_Command_DeckDownload = 1007, -ItemId_Command_ListChatChannels = 1008, -ItemId_Command_ChatJoinChannel = 1009, -ItemId_Command_ChatLeaveChannel = 1010, -ItemId_Command_ChatSay = 1011, -ItemId_Command_ListGames = 1012, -ItemId_Command_CreateGame = 1013, -ItemId_Command_JoinGame = 1014, -ItemId_Command_LeaveGame = 1015, -ItemId_Command_Say = 1016, -ItemId_Command_Shuffle = 1017, -ItemId_Command_Mulligan = 1018, -ItemId_Command_RollDie = 1019, -ItemId_Command_DrawCards = 1020, -ItemId_Command_MoveCard = 1021, -ItemId_Command_FlipCard = 1022, -ItemId_Command_AttachCard = 1023, -ItemId_Command_CreateToken = 1024, -ItemId_Command_CreateArrow = 1025, -ItemId_Command_DeleteArrow = 1026, -ItemId_Command_SetCardAttr = 1027, -ItemId_Command_SetCardCounter = 1028, -ItemId_Command_IncCardCounter = 1029, -ItemId_Command_ReadyStart = 1030, -ItemId_Command_Concede = 1031, -ItemId_Command_IncCounter = 1032, -ItemId_Command_CreateCounter = 1033, -ItemId_Command_SetCounter = 1034, -ItemId_Command_DelCounter = 1035, -ItemId_Command_NextTurn = 1036, -ItemId_Command_SetActivePhase = 1037, -ItemId_Command_DumpZone = 1038, -ItemId_Command_StopDumpZone = 1039, -ItemId_Event_Say = 1040, -ItemId_Event_Leave = 1041, -ItemId_Event_GameClosed = 1042, -ItemId_Event_Shuffle = 1043, -ItemId_Event_RollDie = 1044, -ItemId_Event_MoveCard = 1045, -ItemId_Event_FlipCard = 1046, -ItemId_Event_DestroyCard = 1047, -ItemId_Event_AttachCard = 1048, -ItemId_Event_CreateToken = 1049, -ItemId_Event_DeleteArrow = 1050, -ItemId_Event_SetCardAttr = 1051, -ItemId_Event_SetCardCounter = 1052, -ItemId_Event_SetCounter = 1053, -ItemId_Event_DelCounter = 1054, -ItemId_Event_SetActivePlayer = 1055, -ItemId_Event_SetActivePhase = 1056, -ItemId_Event_DumpZone = 1057, -ItemId_Event_StopDumpZone = 1058, -ItemId_Event_ServerMessage = 1059, -ItemId_Event_GameJoined = 1060, -ItemId_Event_ChatLeaveChannel = 1061, -ItemId_Event_ChatSay = 1062, -ItemId_Context_ReadyStart = 1063, -ItemId_Context_Concede = 1064, -ItemId_Context_DeckSelect = 1065, -ItemId_Other = 1066 +ItemId_Command_Message = 1003, +ItemId_Command_DeckList = 1004, +ItemId_Command_DeckNewDir = 1005, +ItemId_Command_DeckDelDir = 1006, +ItemId_Command_DeckDel = 1007, +ItemId_Command_DeckDownload = 1008, +ItemId_Command_ListChatChannels = 1009, +ItemId_Command_ChatJoinChannel = 1010, +ItemId_Command_ChatLeaveChannel = 1011, +ItemId_Command_ChatSay = 1012, +ItemId_Command_ListGames = 1013, +ItemId_Command_ListUsers = 1014, +ItemId_Command_CreateGame = 1015, +ItemId_Command_JoinGame = 1016, +ItemId_Command_LeaveGame = 1017, +ItemId_Command_Say = 1018, +ItemId_Command_Shuffle = 1019, +ItemId_Command_Mulligan = 1020, +ItemId_Command_RollDie = 1021, +ItemId_Command_DrawCards = 1022, +ItemId_Command_MoveCard = 1023, +ItemId_Command_FlipCard = 1024, +ItemId_Command_AttachCard = 1025, +ItemId_Command_CreateToken = 1026, +ItemId_Command_CreateArrow = 1027, +ItemId_Command_DeleteArrow = 1028, +ItemId_Command_SetCardAttr = 1029, +ItemId_Command_SetCardCounter = 1030, +ItemId_Command_IncCardCounter = 1031, +ItemId_Command_ReadyStart = 1032, +ItemId_Command_Concede = 1033, +ItemId_Command_IncCounter = 1034, +ItemId_Command_CreateCounter = 1035, +ItemId_Command_SetCounter = 1036, +ItemId_Command_DelCounter = 1037, +ItemId_Command_NextTurn = 1038, +ItemId_Command_SetActivePhase = 1039, +ItemId_Command_DumpZone = 1040, +ItemId_Command_StopDumpZone = 1041, +ItemId_Event_Say = 1042, +ItemId_Event_Leave = 1043, +ItemId_Event_GameClosed = 1044, +ItemId_Event_Shuffle = 1045, +ItemId_Event_RollDie = 1046, +ItemId_Event_MoveCard = 1047, +ItemId_Event_FlipCard = 1048, +ItemId_Event_DestroyCard = 1049, +ItemId_Event_AttachCard = 1050, +ItemId_Event_CreateToken = 1051, +ItemId_Event_DeleteArrow = 1052, +ItemId_Event_SetCardAttr = 1053, +ItemId_Event_SetCardCounter = 1054, +ItemId_Event_SetCounter = 1055, +ItemId_Event_DelCounter = 1056, +ItemId_Event_SetActivePlayer = 1057, +ItemId_Event_SetActivePhase = 1058, +ItemId_Event_DumpZone = 1059, +ItemId_Event_StopDumpZone = 1060, +ItemId_Event_ServerMessage = 1061, +ItemId_Event_Message = 1062, +ItemId_Event_GameJoined = 1063, +ItemId_Event_UserLeft = 1064, +ItemId_Event_ChatLeaveChannel = 1065, +ItemId_Event_ChatSay = 1066, +ItemId_Context_ReadyStart = 1067, +ItemId_Context_Concede = 1068, +ItemId_Context_DeckSelect = 1069, +ItemId_Other = 1070 }; diff --git a/common/protocol_items.cpp b/common/protocol_items.cpp index 3d3cca24..9c5c8d59 100644 --- a/common/protocol_items.cpp +++ b/common/protocol_items.cpp @@ -11,6 +11,12 @@ Command_Login::Command_Login(const QString &_username, const QString &_password) insertItem(new SerializableItem_String("username", _username)); insertItem(new SerializableItem_String("password", _password)); } +Command_Message::Command_Message(const QString &_userName, const QString &_text) + : Command("message") +{ + insertItem(new SerializableItem_String("user_name", _userName)); + insertItem(new SerializableItem_String("text", _text)); +} Command_DeckList::Command_DeckList() : Command("deck_list") { @@ -58,6 +64,10 @@ Command_ListGames::Command_ListGames() : Command("list_games") { } +Command_ListUsers::Command_ListUsers() + : Command("list_users") +{ +} Command_CreateGame::Command_CreateGame(const QString &_description, const QString &_password, int _maxPlayers, bool _spectatorsAllowed, bool _spectatorsNeedPassword, bool _spectatorsCanTalk, bool _spectatorsSeeEverything) : Command("create_game") { @@ -370,6 +380,13 @@ Event_ServerMessage::Event_ServerMessage(const QString &_message) { insertItem(new SerializableItem_String("message", _message)); } +Event_Message::Event_Message(const QString &_senderName, const QString &_receiverName, const QString &_text) + : GenericEvent("message") +{ + insertItem(new SerializableItem_String("sender_name", _senderName)); + insertItem(new SerializableItem_String("receiver_name", _receiverName)); + insertItem(new SerializableItem_String("text", _text)); +} Event_GameJoined::Event_GameJoined(int _gameId, const QString &_gameDescription, int _playerId, bool _spectator, bool _spectatorsCanTalk, bool _spectatorsSeeEverything, bool _resuming) : GenericEvent("game_joined") { @@ -381,6 +398,11 @@ Event_GameJoined::Event_GameJoined(int _gameId, const QString &_gameDescription, insertItem(new SerializableItem_Bool("spectators_see_everything", _spectatorsSeeEverything)); insertItem(new SerializableItem_Bool("resuming", _resuming)); } +Event_UserLeft::Event_UserLeft(const QString &_userName) + : GenericEvent("user_left") +{ + insertItem(new SerializableItem_String("user_name", _userName)); +} Event_ChatLeaveChannel::Event_ChatLeaveChannel(const QString &_channel, const QString &_playerName) : ChatEvent("chat_leave_channel", _channel) { @@ -409,6 +431,7 @@ void ProtocolItem::initializeHashAuto() { itemNameHash.insert("cmdping", Command_Ping::newItem); itemNameHash.insert("cmdlogin", Command_Login::newItem); + itemNameHash.insert("cmdmessage", Command_Message::newItem); itemNameHash.insert("cmddeck_list", Command_DeckList::newItem); itemNameHash.insert("cmddeck_new_dir", Command_DeckNewDir::newItem); itemNameHash.insert("cmddeck_del_dir", Command_DeckDelDir::newItem); @@ -419,6 +442,7 @@ void ProtocolItem::initializeHashAuto() itemNameHash.insert("cmdchat_leave_channel", Command_ChatLeaveChannel::newItem); itemNameHash.insert("cmdchat_say", Command_ChatSay::newItem); itemNameHash.insert("cmdlist_games", Command_ListGames::newItem); + itemNameHash.insert("cmdlist_users", Command_ListUsers::newItem); itemNameHash.insert("cmdcreate_game", Command_CreateGame::newItem); itemNameHash.insert("cmdjoin_game", Command_JoinGame::newItem); itemNameHash.insert("cmdleave_game", Command_LeaveGame::newItem); @@ -466,7 +490,9 @@ void ProtocolItem::initializeHashAuto() itemNameHash.insert("game_eventdump_zone", Event_DumpZone::newItem); itemNameHash.insert("game_eventstop_dump_zone", Event_StopDumpZone::newItem); itemNameHash.insert("generic_eventserver_message", Event_ServerMessage::newItem); + itemNameHash.insert("generic_eventmessage", Event_Message::newItem); itemNameHash.insert("generic_eventgame_joined", Event_GameJoined::newItem); + itemNameHash.insert("generic_eventuser_left", Event_UserLeft::newItem); itemNameHash.insert("chat_eventchat_leave_channel", Event_ChatLeaveChannel::newItem); itemNameHash.insert("chat_eventchat_say", Event_ChatSay::newItem); itemNameHash.insert("game_event_contextready_start", Context_ReadyStart::newItem); diff --git a/common/protocol_items.dat b/common/protocol_items.dat index 45f6b533..5d7b3b11 100644 --- a/common/protocol_items.dat +++ b/common/protocol_items.dat @@ -1,5 +1,6 @@ 0:ping 0:login:s,username:s,password +0:message:s,user_name:s,text 0:deck_list 0:deck_new_dir:s,path:s,dir_name 0:deck_del_dir:s,path @@ -10,6 +11,7 @@ 1:chat_leave_channel 1:chat_say:s,message 0:list_games +0:list_users 0:create_game:s,description:s,password:i,max_players:b,spectators_allowed:b,spectators_need_password:b,spectators_can_talk:b,spectators_see_everything 0:join_game:i,game_id:s,password:b,spectator 2:leave_game @@ -57,7 +59,9 @@ 3:dump_zone:i,zone_owner_id:s,zone:i,number_cards 3:stop_dump_zone:i,zone_owner_id:s,zone 4:server_message:s,message +4:message:s,sender_name:s,receiver_name:s,text 4:game_joined:i,game_id:s,game_description:i,player_id:b,spectator:b,spectators_can_talk:b,spectators_see_everything:b,resuming +4:user_left:s,user_name 5:chat_leave_channel:s,player_name 5:chat_say:s,player_name:s,message 6:ready_start diff --git a/common/protocol_items.h b/common/protocol_items.h index b94c7afb..3edfc650 100644 --- a/common/protocol_items.h +++ b/common/protocol_items.h @@ -19,6 +19,15 @@ public: static SerializableItem *newItem() { return new Command_Login; } int getItemId() const { return ItemId_Command_Login; } }; +class Command_Message : public Command { + Q_OBJECT +public: + Command_Message(const QString &_userName = QString(), const QString &_text = QString()); + QString getUserName() const { return static_cast(itemMap.value("user_name"))->getData(); }; + QString getText() const { return static_cast(itemMap.value("text"))->getData(); }; + static SerializableItem *newItem() { return new Command_Message; } + int getItemId() const { return ItemId_Command_Message; } +}; class Command_DeckList : public Command { Q_OBJECT public: @@ -96,6 +105,13 @@ public: static SerializableItem *newItem() { return new Command_ListGames; } int getItemId() const { return ItemId_Command_ListGames; } }; +class Command_ListUsers : public Command { + Q_OBJECT +public: + Command_ListUsers(); + static SerializableItem *newItem() { return new Command_ListUsers; } + int getItemId() const { return ItemId_Command_ListUsers; } +}; class Command_CreateGame : public Command { Q_OBJECT public: @@ -549,6 +565,16 @@ public: static SerializableItem *newItem() { return new Event_ServerMessage; } int getItemId() const { return ItemId_Event_ServerMessage; } }; +class Event_Message : public GenericEvent { + Q_OBJECT +public: + Event_Message(const QString &_senderName = QString(), const QString &_receiverName = QString(), const QString &_text = QString()); + QString getSenderName() const { return static_cast(itemMap.value("sender_name"))->getData(); }; + QString getReceiverName() const { return static_cast(itemMap.value("receiver_name"))->getData(); }; + QString getText() const { return static_cast(itemMap.value("text"))->getData(); }; + static SerializableItem *newItem() { return new Event_Message; } + int getItemId() const { return ItemId_Event_Message; } +}; class Event_GameJoined : public GenericEvent { Q_OBJECT public: @@ -563,6 +589,14 @@ public: static SerializableItem *newItem() { return new Event_GameJoined; } int getItemId() const { return ItemId_Event_GameJoined; } }; +class Event_UserLeft : public GenericEvent { + Q_OBJECT +public: + Event_UserLeft(const QString &_userName = QString()); + QString getUserName() const { return static_cast(itemMap.value("user_name"))->getData(); }; + static SerializableItem *newItem() { return new Event_UserLeft; } + int getItemId() const { return ItemId_Event_UserLeft; } +}; class Event_ChatLeaveChannel : public ChatEvent { Q_OBJECT public: diff --git a/common/server.cpp b/common/server.cpp index 5a1fa3a1..499dfa4d 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -62,6 +62,12 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString users.insert(name, session); + Event_UserJoined *event = new Event_UserJoined(new ServerInfo_User(data)); + for (int i = 0; i < clients.size(); ++i) + if (clients[i]->getAcceptsUserListChanges()) + clients[i]->sendProtocolItem(event, false); + delete event; + return authState; } @@ -85,8 +91,15 @@ void Server::removeClient(Server_ProtocolHandler *client) { clients.removeAt(clients.indexOf(client)); ServerInfo_User *data = client->getUserInfo(); - if (data) + if (data) { + Event_UserLeft *event = new Event_UserLeft(data->getName()); + for (int i = 0; i < clients.size(); ++i) + if (clients[i]->getAcceptsUserListChanges()) + clients[i]->sendProtocolItem(event, false); + delete event; + users.remove(data->getName()); + } qDebug() << "Server::removeClient: " << clients.size() << "clients; " << users.size() << "users left"; } diff --git a/common/server.h b/common/server.h index c5ad1cd8..81e9c848 100644 --- a/common/server.h +++ b/common/server.h @@ -29,6 +29,7 @@ public: const QMap &getChatChannels() { return chatChannels; } void broadcastGameListUpdate(Server_Game *game); + const QMap &getUsers() const { return users; } void addClient(Server_ProtocolHandler *player); void removeClient(Server_ProtocolHandler *player); virtual QString getLoginMessage() const = 0; diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 58d57608..828051c0 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -14,7 +14,7 @@ #include Server_ProtocolHandler::Server_ProtocolHandler(Server *_server, QObject *parent) - : QObject(parent), server(_server), authState(PasswordWrong), acceptsGameListChanges(false), userInfo(0), lastCommandTime(QDateTime::currentDateTime()) + : QObject(parent), server(_server), authState(PasswordWrong), acceptsGameListChanges(false), acceptsUserListChanges(false), acceptsChatChannelListChanges(false), userInfo(0), lastCommandTime(QDateTime::currentDateTime()) { connect(server, SIGNAL(pingClockTimeout()), this, SLOT(pingClockTimeout())); } @@ -116,6 +116,7 @@ ResponseCode Server_ProtocolHandler::processCommandHelper(Command *command, Comm switch (command->getItemId()) { case ItemId_Command_Ping: return cmdPing(qobject_cast(command), cont); case ItemId_Command_Login: return cmdLogin(qobject_cast(command), cont); + case ItemId_Command_Message: return cmdMessage(qobject_cast(command), cont); case ItemId_Command_DeckList: return cmdDeckList(qobject_cast(command), cont); case ItemId_Command_DeckNewDir: return cmdDeckNewDir(qobject_cast(command), cont); case ItemId_Command_DeckDelDir: return cmdDeckDelDir(qobject_cast(command), cont); @@ -124,6 +125,7 @@ ResponseCode Server_ProtocolHandler::processCommandHelper(Command *command, Comm case ItemId_Command_DeckDownload: return cmdDeckDownload(qobject_cast(command), cont); case ItemId_Command_ListChatChannels: return cmdListChatChannels(qobject_cast(command), cont); case ItemId_Command_ChatJoinChannel: return cmdChatJoinChannel(qobject_cast(command), cont); + case ItemId_Command_ListUsers: return cmdListUsers(qobject_cast(command), cont); case ItemId_Command_ListGames: return cmdListGames(qobject_cast(command), cont); case ItemId_Command_CreateGame: return cmdCreateGame(qobject_cast(command), cont); case ItemId_Command_JoinGame: return cmdJoinGame(qobject_cast(command), cont); @@ -229,6 +231,21 @@ ResponseCode Server_ProtocolHandler::cmdLogin(Command_Login *cmd, CommandContain return RespOk; } +ResponseCode Server_ProtocolHandler::cmdMessage(Command_Message *cmd, CommandContainer *cont) +{ + if (authState == PasswordWrong) + return RespLoginNeeded; + + QString receiver = cmd->getUserName(); + Server_ProtocolHandler *userHandler = server->getUsers().value(receiver); + if (!userHandler) + return RespNameNotFound; + + cont->enqueueItem(new Event_Message(userInfo->getName(), receiver, cmd->getText())); + userHandler->sendProtocolItem(new Event_Message(userInfo->getName(), receiver, cmd->getText())); + return RespOk; +} + ResponseCode Server_ProtocolHandler::cmdListChatChannels(Command_ListChatChannels * /*cmd*/, CommandContainer *cont) { if (authState == PasswordWrong) @@ -277,6 +294,22 @@ ResponseCode Server_ProtocolHandler::cmdChatSay(Command_ChatSay *cmd, CommandCon return RespOk; } +ResponseCode Server_ProtocolHandler::cmdListUsers(Command_ListUsers * /*cmd*/, CommandContainer *cont) +{ + if (authState == PasswordWrong) + return RespLoginNeeded; + + QList resultList; + QMapIterator userIterator = server->getUsers(); + while (userIterator.hasNext()) + resultList.append(new ServerInfo_User(userIterator.next().value()->getUserInfo())); + + acceptsUserListChanges = true; + + cont->setResponse(new Response_ListUsers(cont->getCmdId(), RespOk, resultList)); + return RespNothing; +} + ResponseCode Server_ProtocolHandler::cmdListGames(Command_ListGames * /*cmd*/, CommandContainer *cont) { if (authState == PasswordWrong) diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index 78a87c10..5a54ccde 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -24,6 +24,7 @@ protected: AuthenticationResult authState; bool acceptsGameListChanges; + bool acceptsUserListChanges; bool acceptsChatChannelListChanges; ServerInfo_User *userInfo; @@ -36,6 +37,7 @@ private: ResponseCode cmdPing(Command_Ping *cmd, CommandContainer *cont); ResponseCode cmdLogin(Command_Login *cmd, CommandContainer *cont); + ResponseCode cmdMessage(Command_Message *cmd, CommandContainer *cont); virtual ResponseCode cmdDeckList(Command_DeckList *cmd, CommandContainer *cont) = 0; virtual ResponseCode cmdDeckNewDir(Command_DeckNewDir *cmd, CommandContainer *cont) = 0; virtual ResponseCode cmdDeckDelDir(Command_DeckDelDir *cmd, CommandContainer *cont) = 0; @@ -46,6 +48,7 @@ private: ResponseCode cmdChatJoinChannel(Command_ChatJoinChannel *cmd, CommandContainer *cont); ResponseCode cmdChatLeaveChannel(Command_ChatLeaveChannel *cmd, CommandContainer *cont, Server_ChatChannel *channel); ResponseCode cmdChatSay(Command_ChatSay *cmd, CommandContainer *cont, Server_ChatChannel *channel); + ResponseCode cmdListUsers(Command_ListUsers *cmd, CommandContainer *cont); ResponseCode cmdListGames(Command_ListGames *cmd, CommandContainer *cont); ResponseCode cmdCreateGame(Command_CreateGame *cmd, CommandContainer *cont); ResponseCode cmdJoinGame(Command_JoinGame *cmd, CommandContainer *cont); @@ -91,6 +94,7 @@ public: void playerRemovedFromGame(Server_Game *game); bool getAcceptsGameListChanges() const { return acceptsGameListChanges; } + bool getAcceptsUserListChanges() const { return acceptsUserListChanges; } bool getAcceptsChatChannelListChanges() const { return acceptsChatChannelListChanges; } ServerInfo_User *getUserInfo() const { return userInfo; } void setUserInfo(ServerInfo_User *_userInfo) { userInfo = _userInfo; }