Merge ssh://cockatrice.git.sourceforge.net/gitroot/cockatrice

This commit is contained in:
Max-Wilhelm Bruker 2009-07-01 16:27:40 +02:00
commit 7023b95db0
19 changed files with 157 additions and 36 deletions

110
cockatrice/doc/protocol.txt Normal file
View file

@ -0,0 +1,110 @@
1. Abstract
The Cockatrice protocol is a client/server protocol intended for communication between
a card game client and a suitable server. It is designed with the goal in mind to make
playing card games, such as Magic the Gathering, over a network easy while eliminating
the possibility of unfair play. Because of that, the server stores all hidden information
and transmits pieces of it to clients only when necessary.
2. Protocol structure
All communication is done over a TCP/IP connection. The protocol is text based.
Strings are encoded in UTF-8 and have UNIX-style line endings (\n).
There are four distinct types of messages:
- Command (section 3)
- Response (section 4)
- Event (section 5)
- List (section 6)
3. Commands
A command can only be sent from client to server and has the following structure:
{ID}|{command}|{parameter1}|{parameter2}...
"ID" is an arbitrary number to be chosen uniquely for each command.
"command" is a command identifier (see section 3).
It depends on the command identifier what has to be passed as parameters.
3.1 ping
Flags:
none
Parameters:
none
Valid response codes:
ok
No effect.
3.2 login
Flags:
none
Parameters:
User name (string)
Password (string)
Valid response codes:
ok
password
If the supplied credentials are correct, "ok" is returned and the connection state
is set to authenticated. (The server is not required to actually check the validity
of the credentials.)
Otherwise, "password" is returned.
3.3 list_games
Flags:
login needed
Parameters:
none
Valid response codes:
ok
If the connection state is unauthenticated, "login_needed" is returned.
Otherwise, "ok" is returned and for each game currently, a list_games event XXX is
sent to the client. The "accepts game list changes" flag of the connection is set.
3.4 create_game
3.5 join_game
3.6 leave_game
3.7 list_players
3.8 say
3.9 submit_deck
3.10 ready_start
3.11 shuffle
3.12 draw_cards
3.13 reveal_card
3.14 move_card
3.15 create_token
3.16 set_card_attr
3.17 inc_counter
3.18 add_counter
3.19 set_counter
3.20 del_counter
3.21 list_counters
3.22 list_zones
3.23 dump_zone
3.24 roll_dice
3.25 set_active_player
3.26 set_active_phase
4. Responses
After processing any command, the server sends a response to the client, indicating
whether the command was understood and valid.
A response can only be sent from server to client and has the following structure:
resp|{ID}|{resp-code}
{ID} is the identifier belonging to the command in question.
{resp-code} contains information about the processing of the command. It can have the
following values:
ok (Success)
login_needed (Error: Command requires login)
syntax (Error: Invalid command or parameters)
context (Error: Command cannot be applied here)
password (Error: Wrong login data)
The response code "syntax" is valid as a response to any command and is
hence not explicitly listed in section 3. The response code "login_needed" applies
to all commands with the "login needed" flag.

View file

@ -70,7 +70,7 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
void CardItem::setName(const QString &_name)
{
name = _name;
update(boundingRect());
update();
}
void CardItem::setTapped(bool _tapped)
@ -80,31 +80,31 @@ void CardItem::setTapped(bool _tapped)
setTransform(QTransform().translate((float) CARD_WIDTH / 2, (float) CARD_HEIGHT / 2).rotate(90).translate((float) -CARD_WIDTH / 2, (float) -CARD_HEIGHT / 2));
else
setTransform(QTransform());
update(boundingRect());
update();
}
void CardItem::setAttacking(bool _attacking)
{
attacking = _attacking;
update(boundingRect());
update();
}
void CardItem::setFaceDown(bool _facedown)
{
facedown = _facedown;
update(boundingRect());
update();
}
void CardItem::setCounters(int _counters)
{
counters = _counters;
update(boundingRect());
update();
}
void CardItem::setAnnotation(const QString &_annotation)
{
annotation = _annotation;
update(boundingRect());
update();
}
void CardItem::setDoesntUntap(bool _doesntUntap)
@ -120,7 +120,7 @@ void CardItem::resetState()
annotation = QString();
setTapped(false);
setDoesntUntap(false);
update(boundingRect());
update();
}
CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown)
@ -215,7 +215,7 @@ QVariant CardItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QV
return value;
} else if (change == ItemSelectedHasChanged) {
qDebug("selection changed");
update(boundingRect());
update();
return value;
} else
return QGraphicsItem::itemChange(change, value);

View file

@ -1,4 +1,5 @@
#include "cardlist.h"
#include "carditem.h"
CardList::CardList(bool _contentsKnown)
: QList<CardItem *>(), contentsKnown(_contentsKnown)

View file

@ -1,9 +1,10 @@
#ifndef CARDLIST_H
#define CARDLIST_H
#include "carditem.h"
#include <QList>
class CardItem;
class CardList : public QList<CardItem *> {
protected:
bool contentsKnown;

View file

@ -6,7 +6,7 @@
#include "zoneviewzone.h"
CardZone::CardZone(Player *_p, const QString &_name, bool _hasCardAttr, bool _isShufflable, QGraphicsItem *parent, bool isView)
: AbstractGraphicsItem(parent), player(_p), name(_name), cards(NULL), view(NULL), menu(NULL), hasCardAttr(_hasCardAttr), isShufflable(_isShufflable)
: AbstractGraphicsItem(parent), player(_p), name(_name), cards(NULL), view(NULL), menu(NULL), doubleClickAction(0), hasCardAttr(_hasCardAttr), isShufflable(_isShufflable)
{
if (!isView)
player->addZone(this);
@ -27,7 +27,7 @@ void CardZone::clearContents()
cards->clear();
}
void CardZone::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
void CardZone::mouseDoubleClickEvent(QGraphicsSceneMouseEvent */*event*/)
{
if (doubleClickAction)
doubleClickAction->trigger();
@ -71,8 +71,7 @@ CardItem *CardZone::getCard(int cardId, const QString &cardName)
CardItem *CardZone::takeCard(int position, int cardId, const QString &cardName)
{
if (position >= cards->size())
return NULL;
Q_ASSERT(position < cards->size());
CardItem *c = cards->takeAt(position);

View file

@ -3,6 +3,7 @@
#include <QString>
#include "cardlist.h"
#include "carditem.h"
#include "abstractgraphicsitem.h"
class Player;

View file

@ -26,7 +26,7 @@ void Counter::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*
void Counter::setValue(int _value)
{
value = _value;
update(boundingRect());
update();
}
void Counter::mousePressEvent(QGraphicsSceneMouseEvent *event)

View file

@ -144,7 +144,7 @@ void Game::initSayMenu()
Player *Game::addPlayer(int playerId, const QString &playerName, QPointF base, bool local)
{
Player *newPlayer = new Player(playerName, playerId, base, local, db, client, scene);
Player *newPlayer = new Player(playerName, playerId, base, local, db, client, scene, this);
connect(newPlayer, SIGNAL(hoverCard(QString)), this, SIGNAL(hoverCard(QString)));
connect(newPlayer, SIGNAL(sigShowCardMenu(QPoint)), this, SLOT(showCardMenu(QPoint)));
@ -346,7 +346,8 @@ void Game::actTap()
QListIterator<QGraphicsItem *> i(scene->selectedItems());
while (i.hasNext()) {
CardItem *temp = (CardItem *) i.next();
client->setCardAttr(qgraphicsitem_cast<CardZone *>(temp->parentItem())->getName(), temp->getId(), "tapped", "1");
if (!temp->getTapped())
client->setCardAttr(qgraphicsitem_cast<CardZone *>(temp->parentItem())->getName(), temp->getId(), "tapped", "1");
}
}
@ -355,7 +356,8 @@ void Game::actUntap()
QListIterator<QGraphicsItem *> i(scene->selectedItems());
while (i.hasNext()) {
CardItem *temp = (CardItem *) i.next();
client->setCardAttr(qgraphicsitem_cast<CardZone *>(temp->parentItem())->getName(), temp->getId(), "tapped", "0");
if (temp->getTapped())
client->setCardAttr(qgraphicsitem_cast<CardZone *>(temp->parentItem())->getName(), temp->getId(), "tapped", "0");
}
}

View file

@ -40,8 +40,6 @@ private slots:
void actEditMessages();
void showCardMenu(QPoint p);
void actTap();
void actUntap();
void actDoesntUntap();
void actFlip();
void actAddCounter();
@ -54,6 +52,9 @@ private slots:
void gameEvent(const ServerEventData &msg);
void playerListReceived(QList<ServerPlayer *> playerList);
void readyStart();
public slots:
void actTap();
void actUntap();
signals:
void submitDecklist();
void hoverCard(QString name);

View file

@ -59,7 +59,7 @@ void HandZone::addCardImpl(CardItem *card, int x, int /*y*/)
card->setParentItem(this);
card->resetState();
card->setVisible(true);
card->update(card->boundingRect());
card->update();
}
void HandZone::handleDropEvent(int cardId, CardZone *startZone, const QPoint &/*dropPoint*/, bool /*faceDown*/)

View file

@ -50,7 +50,7 @@ void LibraryZone::handleDropEvent(int cardId, CardZone *startZone, const QPoint
void LibraryZone::reorganizeCards()
{
update(boundingRect());
update();
}
void LibraryZone::mousePressEvent(QGraphicsSceneMouseEvent *event)

View file

@ -20,6 +20,7 @@ QRectF PileZone::boundingRect() const
void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
qDebug("PileZone::paint");
if (!cards->isEmpty()) {
painter->save();
cards->at(0)->paint(painter, option, widget);
@ -47,7 +48,7 @@ void PileZone::handleDropEvent(int cardId, CardZone *startZone, const QPoint &/*
void PileZone::reorganizeCards()
{
qDebug(QString("PileZone: reorganize, x=%1, y=%2, w=%3, h=%4").arg(boundingRect().x()).arg(boundingRect().y()).arg(boundingRect().width()).arg(boundingRect().height()).toLatin1());
update(boundingRect());
update();
}
void PileZone::mousePressEvent(QGraphicsSceneMouseEvent *event)

View file

@ -4,11 +4,12 @@
#include "playerarea.h"
#include "counter.h"
#include "zoneviewzone.h"
#include "game.h"
#include <QGraphicsScene>
#include <QMenu>
Player::Player(const QString &_name, int _id, QPointF _base, bool _local, CardDatabase *_db, Client *_client, QGraphicsScene *_scene)
: QObject(), defaultNumberTopCards(3), name(_name), id(_id), base(_base), local(_local), db(_db), client(_client)
Player::Player(const QString &_name, int _id, QPointF _base, bool _local, CardDatabase *_db, Client *_client, QGraphicsScene *_scene, Game *_parent)
: QObject(_parent), defaultNumberTopCards(3), name(_name), id(_id), base(_base), local(_local), db(_db), client(_client)
{
area = new PlayerArea(this);
area->setPos(_base);

View file

@ -12,6 +12,7 @@ class QMenu;
class QAction;
class PlayerArea;
class ZoneViewZone;
class Game;
class Player : public QObject {
Q_OBJECT
@ -57,7 +58,7 @@ public:
PlayerArea *area;
Client *client;
void addZone(CardZone *z);
Player(const QString &_name, int _id, QPointF _base, bool _local, CardDatabase *_db, Client *_client, QGraphicsScene *_scene);
Player(const QString &_name, int _id, QPointF _base, bool _local, CardDatabase *_db, Client *_client, QGraphicsScene *_scene, Game *_parent);
~Player();
QMenu *getPlayerMenu() const { return playerMenu; }
int getId() const { return id; }

View file

@ -31,7 +31,7 @@ void TableZone::addCardImpl(CardItem *card, int x, int y)
qDebug(QString("table: appended %1 at pos %2: zValue = %3, x = %4, y = %5").arg(card->getName()).arg(cards->size() - 1).arg(card->zValue()).arg(x).arg(y).toLatin1());
card->setParentItem(this);
card->setVisible(true);
card->update(card->boundingRect());
card->update();
}
void TableZone::handleDropEvent(int cardId, CardZone *startZone, const QPoint &dropPoint, bool faceDown)
@ -55,9 +55,15 @@ void TableZone::reorganizeCards()
void TableZone::toggleTapped()
{
QListIterator<QGraphicsItem *> i(scene()->selectedItems());
while (i.hasNext()) {
CardItem *temp = (CardItem *) i.next();
setCardAttr(temp->getId(), "tapped", temp->getTapped() ? "0" : "1");
QList<QGraphicsItem *> selectedItems = scene()->selectedItems();
bool tapAll = false;
for (int i = 0; i < selectedItems.size(); i++)
if (!qgraphicsitem_cast<CardItem *>(selectedItems[i])->getTapped()) {
tapAll = true;
break;
}
for (int i = 0; i < selectedItems.size(); i++) {
CardItem *temp = qgraphicsitem_cast<CardItem *>(selectedItems[i]);
setCardAttr(temp->getId(), "tapped", (!temp->getTapped() || tapAll) ? "1" : "0");
}
}

View file

@ -52,7 +52,6 @@ void ZoneViewLayout::removeItem(ZoneViewWidget *item)
{
qDebug("ZoneViewLayout::removeItem");
views.removeAt(views.indexOf(item));
scene()->removeItem(item);
reorganize();
}

View file

@ -75,7 +75,7 @@ void ZoneViewZone::addCardImpl(CardItem *card, int x, int /*y*/)
{
cards->insert(x, card);
card->setParentItem(this);
card->update(card->boundingRect());
card->update();
}
void ZoneViewZone::handleDropEvent(int cardId, CardZone *startZone, const QPoint &/*dropPoint*/, bool /*faceDown*/)
@ -89,8 +89,7 @@ void ZoneViewZone::removeCard(int position)
if (position >= cards->size())
return;
CardItem *card = cards->at(position);
cards->removeAt(position);
CardItem *card = cards->takeAt(position);
delete card;
reorganizeCards();
}

View file

@ -15,7 +15,6 @@ bool ReturnMessage::send(ReturnCode code)
case ReturnSyntaxError: returnCodeString = "syntax"; break;
case ReturnContextError: returnCodeString = "context"; break;
case ReturnPasswordWrong: returnCodeString = "password"; break;
case ReturnNameNotFound: returnCodeString = "name_not_found"; break;
}
s->msg(QString("resp|%1|%2|%3").arg(msg_id)
.arg(success ? "ok" : "err")

View file

@ -9,7 +9,7 @@ private:
unsigned int msg_id;
QString cmd;
public:
enum ReturnCode { ReturnNothing, ReturnOk, ReturnLoginNeeded, ReturnSyntaxError, ReturnContextError, ReturnPasswordWrong, ReturnNameNotFound };
enum ReturnCode { ReturnNothing, ReturnOk, ReturnLoginNeeded, ReturnSyntaxError, ReturnContextError, ReturnPasswordWrong };
ReturnMessage(QObject *parent = 0) : QObject(parent), msg_id(0) { }
unsigned int getMsgId() const { return msg_id; }
void setMsgId(unsigned int _msg_id) { msg_id = _msg_id; }