From ce48af53646cd9e7ec762fc1ac176b3aa620b11d Mon Sep 17 00:00:00 2001 From: manuel Date: Thu, 5 May 2011 00:57:07 +0200 Subject: - refactorized the whole project and made a few subprojects - replaced tcp with enet - added connect dialog - some smaller bugfixes --- pacman-c++/client/mainwidget.cpp | 390 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 pacman-c++/client/mainwidget.cpp (limited to 'pacman-c++/client/mainwidget.cpp') diff --git a/pacman-c++/client/mainwidget.cpp b/pacman-c++/client/mainwidget.cpp new file mode 100644 index 0000000..f6f088b --- /dev/null +++ b/pacman-c++/client/mainwidget.cpp @@ -0,0 +1,390 @@ +#include "mainwidget.h" +#include "actor.h" +#include "block.h" +#include "constants.h" +#include "util.h" +#include "pacman.pb.h" +#include + +MainWidget::MainWidget(QWidget *parent) + : QWidget(parent), m_currentKey(Transmission::none), m_running(false), + m_host(NULL), m_peer(NULL), m_scene(NULL), m_maxplayers(0) +{ + m_host = enet_host_create(NULL, 1, 1, 0, 0); + if (m_host == NULL) + { + QMessageBox::critical(this, "Error", "An error occurred while trying to create an ENet client host"); + qCritical() << "An error occurred while trying to create an ENet client host"; + return; + } + + /* create audio player */ + m_ambientPlayer = new GaplessAudioPlayer(Sound::Ambient, 100, this); + + m_recvTimer = new QTimer(this); + m_recvTimer->setInterval(Constants::tick / 2); + connect(m_recvTimer, SIGNAL(timeout()), this, SLOT(tick())); +} + +MainWidget::~MainWidget() +{ + doDisconnect(); + if (m_host != NULL) + { + enet_host_destroy(m_host); + m_host = NULL; + } +} + +void MainWidget::doConnect(QString srv, unsigned int port) +{ + Color::Color color = connectToServer(srv, port); + if (color == Color::none) + { + QMessageBox::critical(this, "Error", "Failed to connect to server"); + return; + } + + /* create our scene */ + m_scene = new SceneHolder(this); + m_scene->setColor(color); + m_scene->showWaitingForPlayers(true); + createGui(); + + m_recvTimer->start(); + emit connected(true); + qDebug() << "[Connect] mycolor=" << m_scene->color(); +} + +void MainWidget::deleteLayout(QLayout *layout) +{ + if (layout == NULL) + return; + + foreach(QObject *obj, layout->children()) + { + QLayout *inLayout = qobject_cast(obj); + deleteLayout(inLayout); + } + + QLayoutItem *child; + while ((child = layout->takeAt(0)) != NULL) + { + child->widget()->deleteLater(); + delete child; + } + + delete layout; +} + +void MainWidget::doDisconnect() +{ + stopGame(); + m_recvTimer->stop(); + closeENetPeer(); + deleteLayout(layout()); + m_playerScoreLayouts.clear(); + emit connected(false); +} + +bool MainWidget::connected() +{ + return m_peer != NULL; +} + +void MainWidget::createGui() +{ + setFocusPolicy(Qt::StrongFocus); + + /* first one is always the own score */ + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setAlignment(Qt::AlignHCenter | Qt::AlignTop); + QHBoxLayout *scoreLayout = new QHBoxLayout(); + for (unsigned int i = 0; i < m_maxplayers; ++i) + { + QGridLayout *playerLayout = new QGridLayout(); + playerLayout->addWidget(new QLabel("Current:", this), 0, 0); + playerLayout->addWidget(new QLabel("Total:", this), 1, 0); + playerLayout->addWidget(new QLabel("0", this), 0, 1, Qt::AlignLeft); + playerLayout->addWidget(new QLabel("0", this), 1, 1, Qt::AlignLeft); + playerLayout->setColumnStretch(1, 10); + playerLayout->setSizeConstraint(QLayout::SetMinimumSize); + + QGroupBox *scoreBox = new QGroupBox(QString("Player %1").arg(i + 1), this); + scoreBox->setObjectName(QString("actor%1").arg(i + 1)); + scoreBox->setCheckable(true); + scoreBox->setDisabled(i >= m_maxplayers); + connect(scoreBox, SIGNAL(clicked()), this, SLOT(playerScoreClicked())); + + scoreBox->setLayout(playerLayout); + m_playerScoreLayouts.append(playerLayout); + + if (Color::order[i] == m_scene->color()) + scoreLayout->insertWidget(0, scoreBox); + else + scoreLayout->addWidget(scoreBox); + } + layout->addLayout(scoreLayout); + + QGraphicsView *window = new QGraphicsView(m_scene, this); + window->setFrameStyle(0); + window->setFixedSize(m_scene->sceneRect().size().toSize()); + window->setWindowFlags(window->windowFlags() & ~Qt::WindowMaximizeButtonHint); + window->setFocusPolicy(Qt::NoFocus); + layout->addWidget(window, 0, Qt::AlignCenter); + + QFile css(":/stylesheet"); + css.open(QFile::ReadOnly); + qApp->setStyleSheet(QLatin1String(css.readAll())); + + /* add dummy layout at the end which gets streched when resizing */ + QHBoxLayout *spacer = new QHBoxLayout(); + layout->addLayout(spacer, 10); + + setLayout(layout); +} + +void MainWidget::updateScore(const ProtoBuf::MapUpdate& packet) +{ + for(unsigned i = 0; i < m_maxplayers; ++i) + { + QGridLayout *score = m_playerScoreLayouts.at(i); + QLabel *turnPointsLbl = dynamic_cast(score->itemAtPosition(0, 1)->widget()); + turnPointsLbl->setText(QString::number(packet.round_points(i))); + + QLabel *allPointsLbl = dynamic_cast(score->itemAtPosition(1, 1)->widget()); + allPointsLbl->setText(QString::number(packet.game_points(i))); + } +} + +Transmission::field_t MainWidget::translateKey(int key) +{ + switch(key) + { + case Qt::Key_W: + case Qt::Key_Up: + return Transmission::direction_up; + break; + case Qt::Key_S: + case Qt::Key_Down: + return Transmission::direction_down; + break; + case Qt::Key_A: + case Qt::Key_Left: + return Transmission::direction_left; + break; + case Qt::Key_D: + case Qt::Key_Right: + return Transmission::direction_right; + break; + default: + return Transmission::direction_none; + } +} + +void MainWidget::tick() +{ + ENetEvent event; + while (enet_host_service(m_host, &event, 1) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_DISCONNECT: + m_peer = NULL; + doDisconnect(); + break; + case ENET_EVENT_TYPE_RECEIVE: + tick(&event); + enet_packet_destroy(event.packet); + break; + default: + break; + } + } +} + +void MainWidget::tick(ENetEvent *event) +{ + QSharedPointer data = Util::receivePacket(event->packet); + bool worked = m_updatepacket.ParseFromArray(data->data(), data->size()); + Q_ASSERT(worked); + Q_UNUSED(worked); + + /* eating order data set indicates a new round */ + if (m_updatepacket.eating_order_size() > 0) + { + Q_ASSERT(m_scene != NULL); + m_scene->reset(); + + /* fetch eating order */ + QList order; + for(int i = 0; i < m_updatepacket.eating_order_size(); ++i) + order.append(static_cast(m_updatepacket.eating_order(i) & Transmission::color_mask)); + m_scene->setEatingOrder(order); + + /* stop game */ + stopGame(); + + /* and restart game */ + QTimer *timer = new QTimer(this); + timer->setSingleShot(true); + timer->setInterval(Sound::length[Sound::Intro] + Constants::tick); + connect(timer, SIGNAL(timeout()), this, SLOT(startGame())); + timer->start(); + AudioManager::self()->play(Sound::Intro, true); + } + + Transmission::map_t map = Util::createUninitialisedMap(); + Q_ASSERT(m_updatepacket.field_size() == (int) (Constants::map_size.width * Constants::map_size.height)); + int i = 0; + for (unsigned int x = 0; x < Constants::map_size.width; ++x) + { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) + { + map[x][y] = m_updatepacket.field(i); + ++i; + } + } + m_scene->updateMap(map); + Util::deleteMap(map); + updateScore(m_updatepacket); + + if (m_updatepacket.eating_order_size() > 0) + { + m_scene->showWaitingForPlayers(false); + m_scene->showEatingText(); + } +} + +void MainWidget::keyPressEvent(QKeyEvent* event) +{ + if (event->isAutoRepeat()) + return; + + QWidget::keyPressEvent(event); + Transmission::field_t newKey = translateKey(event->key()); + if (newKey == Transmission::direction_none) + return; + bool sendUpdate = (m_currentKey != newKey); + m_currentKey = newKey; + if (sendUpdate) + sendKeyUpdate(); +} + +void MainWidget::sendKeyUpdate() +{ + if (m_currentKey == Transmission::direction_none) + return; + qDebug() << "[SendKey] key=" << m_currentKey; + ProtoBuf::KeyPressUpdate packet; + packet.set_newkey(m_currentKey); + Util::sendPacket(packet, m_peer, m_host); +} + +void MainWidget::keyReleaseEvent(QKeyEvent* event) +{ + if (event->isAutoRepeat()) + return; + + QWidget::keyReleaseEvent(event); + m_currentKey = Transmission::none; + return; +} + +void MainWidget::startGame() +{ + disconnect(AudioManager::self()->audioPlayer(), NULL, this, SLOT(startGame())); + m_scene->showEatingText(false); + m_running = true; + sendKeyUpdate(); + m_ambientPlayer->play(); +} + +void MainWidget::stopGame() +{ + m_running = false; + m_ambientPlayer->pause(); +} + +void MainWidget::setAmbientMuted(bool muted) +{ + m_ambientPlayer->setMuted(muted); +} + +void MainWidget::playerScoreClicked() +{ + QGroupBox *tmp = qobject_cast(sender()); + tmp->setChecked(true); + return; +} + +Color::Color MainWidget::connectToServer(QString srv, unsigned int port) +{ + qDebug() << "[Connect] server=" << srv << "port=" << port; + + /* connect to server */ + closeENetPeer(); + ENetAddress address; + enet_address_set_host(&address, qPrintable(srv)); + address.port = Constants::Networking::port; + m_peer = enet_host_connect(m_host, &address, 1, 0); + if (m_peer == NULL) + { + qCritical() << "No available peers for initiating an ENet connection"; + return Color::none; + } + + ENetEvent event; + bool worked = (enet_host_service(m_host, &event, Constants::Networking::connection_timeout) > 0 + && event.type == ENET_EVENT_TYPE_CONNECT); + if (worked) + { + /* additional init: first packet is our color */ + worked = (enet_host_service(m_host, &event, Constants::Networking::packet_timeout) > 0 + && event.type == ENET_EVENT_TYPE_RECEIVE); + if (worked) + { + /* receive color */ + QSharedPointer data = Util::receivePacket(event.packet); + enet_packet_destroy(event.packet); + ProtoBuf::Init packet; + worked = packet.ParseFromArray(data->data(), data->size()); + Q_ASSERT(worked); + Q_UNUSED(worked); + m_maxplayers = packet.maxplayers(); + return static_cast(packet.color() & Transmission::color_mask); + } + } + enet_peer_reset(m_peer); + m_peer = NULL; + return Color::none; +} + +void MainWidget::closeENetPeer() +{ + if (m_peer != NULL && m_peer->state != ENET_PEER_STATE_DISCONNECTED) + { + /* allow up to 3 seconds for the disconnect to succeed + * and drop any packets received packets + */ + enet_peer_disconnect(m_peer, 0); + ENetEvent event; + while (enet_host_service(m_host, &event, 3000) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_RECEIVE: + enet_packet_destroy(event.packet); + break; + case ENET_EVENT_TYPE_DISCONNECT: + m_peer = NULL; + return; + default: + break; + } + } + + enet_peer_reset(m_peer); + } + m_peer = NULL; +} -- cgit v1.2.3