#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::Networking::recv_interval); connect(m_recvTimer, SIGNAL(timeout()), this, SLOT(readServerUpdates())); } MainWidget::~MainWidget() { doDisconnect(); deleteGUI(); if (m_host != NULL) { enet_host_destroy(m_host); m_host = NULL; } } void MainWidget::doConnect(QString srv, unsigned int port) { /* preload sound - this eliminates some loading gap on slower computers */ AudioManager::self()->setSource(Sound::Intro); deleteGUI(); 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(); 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::deleteGUI() { deleteLayout(layout()); m_playerScoreLayouts.clear(); } void MainWidget::doDisconnect() { if (!connected()) return; closeENetPeer(); onDisconnect(); } void MainWidget::onDisconnect() { stopGame(); m_recvTimer->stop(); m_scene->clear(); m_scene->showWonLost(didIWinLoose()); 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))); } } bool MainWidget::didIWinLoose() { QGridLayout *score = m_playerScoreLayouts.at(0); QLabel *allPointsLbl = dynamic_cast(score->itemAtPosition(1, 1)->widget()); unsigned long myscore = allPointsLbl->text().toULong(); for(unsigned i = 1; i < m_maxplayers; ++i) { QGridLayout *score = m_playerScoreLayouts.at(i); QLabel *allPointsLbl = dynamic_cast(score->itemAtPosition(1, 1)->widget()); unsigned long other = allPointsLbl->text().toULong(); if (other > myscore) return false; } return true; } 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::readServerUpdates() { ENetEvent event; while (enet_host_service(m_host, &event, 1) > 0) { switch (event.type) { case ENET_EVENT_TYPE_DISCONNECT: m_peer = NULL; onDisconnect(); break; case ENET_EVENT_TYPE_RECEIVE: processServerUpdates(&event); enet_packet_destroy(event.packet); break; default: break; } } } void MainWidget::processServerUpdates(ENetEvent *event) { QSharedPointer data = Util::receivePacket(event->packet); if (!m_updatepacket.ParseFromArray(data->data(), data->size())) { qWarning() << "Invalid update packet from server"; return; } /* eating order data set indicates a new round */ if (m_updatepacket.eating_order_size() > 0) { Q_ASSERT(m_scene != NULL); /* stop game */ stopGame(); /* 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); /* 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); } 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->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 (!connected()) return; if (m_currentKey == Transmission::direction_none) return; qDebug() << "[SendKey] key=" << m_currentKey; ProtoBuf::ClientUpdate packet; packet.set_new_direction(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->hideOverlayText(); m_running = true; sendKeyUpdate(); m_ambientPlayer->play(); } void MainWidget::stopGame() { m_running = false; m_ambientPlayer->pause(); m_scene->reset(); } 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; if (packet.ParseFromArray(data->data(), data->size())) { m_maxplayers = packet.maxplayers(); return static_cast(packet.color() & Transmission::color_mask); } qWarning() << "Invalid initialize packet from server"; } } 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; }