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/clicklabel.cpp | 12 ++ pacman-c++/client/clicklabel.h | 21 +++ pacman-c++/client/client.cpp | 265 ++++++++++++++++++++++++++ pacman-c++/client/client.h | 41 ++++ pacman-c++/client/client.pro | 12 ++ pacman-c++/client/mainwidget.cpp | 390 +++++++++++++++++++++++++++++++++++++++ pacman-c++/client/mainwidget.h | 80 ++++++++ 7 files changed, 821 insertions(+) create mode 100644 pacman-c++/client/clicklabel.cpp create mode 100644 pacman-c++/client/clicklabel.h create mode 100644 pacman-c++/client/client.cpp create mode 100644 pacman-c++/client/client.h create mode 100644 pacman-c++/client/client.pro create mode 100644 pacman-c++/client/mainwidget.cpp create mode 100644 pacman-c++/client/mainwidget.h (limited to 'pacman-c++/client') diff --git a/pacman-c++/client/clicklabel.cpp b/pacman-c++/client/clicklabel.cpp new file mode 100644 index 0000000..87b06b8 --- /dev/null +++ b/pacman-c++/client/clicklabel.cpp @@ -0,0 +1,12 @@ +#include "clicklabel.h" + +ClickLabel::ClickLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) + : QLabel(text, parent, f) +{ +} + +void ClickLabel::mouseReleaseEvent(QMouseEvent * /* event */) +{ + emit clicked(); +} + diff --git a/pacman-c++/client/clicklabel.h b/pacman-c++/client/clicklabel.h new file mode 100644 index 0000000..494b1ee --- /dev/null +++ b/pacman-c++/client/clicklabel.h @@ -0,0 +1,21 @@ +#ifndef CLICKLABEL_H +#define CLICKLABEL_H + +#include + +class ClickLabel + : public QLabel +{ + Q_OBJECT + +public: + ClickLabel(const QString &text, QWidget *parent = 0, Qt::WindowFlags f = 0); + +signals: + void clicked(); + +protected: + void mouseReleaseEvent(QMouseEvent *event); +}; + +#endif // CLICKLABEL_H diff --git a/pacman-c++/client/client.cpp b/pacman-c++/client/client.cpp new file mode 100644 index 0000000..d064d9e --- /dev/null +++ b/pacman-c++/client/client.cpp @@ -0,0 +1,265 @@ +#include "client.h" +#include "clicklabel.h" +#include "audio.h" +#include "util.h" +#include "pacman.pb.h" + +extern "C" { +#include "enet/enet.h" +} + +Client::Client() + : m_ambientMuted(false) +{ + m_settings = new QSettings(qApp->organizationName(), qApp->applicationName(), this); + m_dialog = new QDialog(this); + m_mainWidget = new MainWidget(this); + createMenu(); + m_mainWidget->setAmbientMuted(m_ambientMuted); + setCentralWidget(m_mainWidget); + showConnectDialog(); + m_dialog->setFocus(); +} + +void Client::createMenu() +{ + QMenu *fileMenu = menuBar()->addMenu("&File"); + + bool sound = AudioManager::self()->isWorking(); + bool muted = !sound || m_settings->value("muted", false).toBool(); + AudioManager::self()->setMuted(muted); + + /* toggle sound: corner icon */ + ClickLabel *toggleSound = new ClickLabel("Toggle Sound", this); + toggleSound->setToolTip("Toggle Sound"); + toggleSound->setFixedWidth(20); + toggleSound->setFixedHeight(16); + toggleSound->setAlignment(Qt::AlignBottom); + toggleSound->setPixmap(soundIcon(!muted)); + if (sound) + { + connect(toggleSound, SIGNAL(clicked()), this, SLOT(toggleSound())); + connect(AudioManager::self(), SIGNAL(mutedChanged(bool)), this, SLOT(mutedChanged(bool))); + } + menuBar()->setCornerWidget(toggleSound); + + /* toggle sound: menu */ + QAction *toggleSoundAction = new QAction("&Sound", this); + toggleSoundAction->setToolTip("Toggle Sound"); + toggleSoundAction->setCheckable(true); + toggleSoundAction->setChecked(!muted); + toggleSoundAction->setDisabled(!sound); + fileMenu->addAction(toggleSoundAction); + if (sound) + { + connect(toggleSoundAction, SIGNAL(triggered()), this, SLOT(toggleSound())); + connect(this, SIGNAL(setMuteActionsChecked(bool)), toggleSoundAction, SLOT(setChecked(bool))); + } + + /* toggle ambient sound: menu */ + m_ambientMuted = muted || m_settings->value("ambientMuted", false).toBool(); + QAction *toggleAmbientAction = new QAction("&Ambient Sound", this); + toggleAmbientAction->setToolTip("Toggle Ambient Sound"); + toggleAmbientAction->setCheckable(true); + toggleAmbientAction->setChecked(!m_ambientMuted); + toggleAmbientAction->setDisabled(muted); + fileMenu->addAction(toggleAmbientAction); + if (sound) + { + connect(toggleAmbientAction, SIGNAL(triggered(bool)), this, SLOT(enableAmbientSound(bool))); + connect(this, SIGNAL(setMuteActionsChecked(bool)), toggleAmbientAction, SLOT(setEnabled(bool))); + } + + /* connect/disconnect entry */ + fileMenu->addSeparator(); + QAction *connectAction = new QAction("C&onnect", this); + fileMenu->addAction(connectAction); + connect(connectAction, SIGNAL(triggered()), this, SLOT(showConnectDialog())); + connect(m_mainWidget, SIGNAL(connected(bool)), connectAction, SLOT(setDisabled(bool))); + + QAction *disconnectAction = new QAction("Di&sconnect", this); + disconnectAction->setDisabled(true); + fileMenu->addAction(disconnectAction); + connect(disconnectAction, SIGNAL(triggered()), m_mainWidget, SLOT(doDisconnect())); + connect(m_mainWidget, SIGNAL(connected(bool)), disconnectAction, SLOT(setEnabled(bool))); + + /* exit entry */ + fileMenu->addSeparator(); + QAction *quitAction = new QAction("E&xit", this); + quitAction->setIcon(QIcon::fromTheme(QLatin1String("application-exit"))); + fileMenu->addAction(quitAction); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + + QAction *aboutAction= menuBar()->addAction("Ab&out"); + connect(aboutAction, SIGNAL(triggered()), this, SLOT(showAbout())); +} + +void Client::toggleSound() +{ + if (!AudioManager::self()->isWorking()) + return; + bool muted = !AudioManager::self()->isMuted(); + AudioManager::self()->setMuted(muted); + /* mute ambient sound again if explicitly muted */ + if (!muted && m_ambientMuted) + m_mainWidget->setAmbientMuted(true); +} + +void Client::mutedChanged(bool muted) +{ + ClickLabel *tmp = qobject_cast(menuBar()->cornerWidget()); + tmp->setPixmap(soundIcon(!muted)); + m_settings->setValue("muted", muted); + emit setMuteActionsChecked(!muted); +} + +void Client::enableAmbientSound(bool enabled) +{ + if (!AudioManager::self()->isWorking()) + return; + m_ambientMuted = !enabled; + m_mainWidget->setAmbientMuted(m_ambientMuted); + m_settings->setValue("ambientMuted", m_ambientMuted); +} + +QPixmap Client::soundIcon(bool enabled) const +{ + QImage img(enabled ? ":/soundon" : ":/soundoff"); + img.setColor(1, menuBar()->palette().color( + enabled ? QPalette::Active : QPalette::Disabled, + QPalette::ButtonText).rgba()); + return QPixmap::fromImage(img); +} + +void Client::showAbout() +{ + if (m_dialog != NULL) + { + delete m_dialog; + m_dialog = new QDialog(this); + } + m_dialog->setWindowTitle("About Pacman"); + m_dialog->setWindowFlags(m_dialog->windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QGridLayout *layout = new QGridLayout(m_dialog); + layout->setSizeConstraint(QLayout::SetFixedSize); + + QString actoricons; + for(int i = 0; Color::order[i] != Color::none; ++i) + actoricons += QString("").arg(i + 1); + + const QString text = QString( + "

Multiplayer Pacman %1

" + "Authors: H. Demel, B. Mallinger, M. Mausz, M. Racz
" + "
" + "Gameplay based on Pacman" + ", © Namco 1980
" + "
" + "Developed using Qt %2 (%3 bit)
") + .arg(actoricons, QLatin1String(QT_VERSION_STR), QString::number(QSysInfo::WordSize)); + + QLabel *label = new QLabel(text, m_dialog); + label->setWordWrap(true); + label->setOpenExternalLinks(true); + label->setTextInteractionFlags(Qt::TextBrowserInteraction); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, m_dialog); + QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); + buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole | QDialogButtonBox::AcceptRole)); + connect(buttonBox , SIGNAL(rejected()), m_dialog, SLOT(reject())); + + layout->addWidget(label, 0, 1, 4, 4); + layout->addWidget(buttonBox, 4, 0, 1, 5); + m_dialog->show(); +} + +void Client::showConnectDialog() +{ + if (m_dialog != NULL) + { + delete m_dialog; + m_dialog = new QDialog(this); + } + m_dialog->setWindowTitle("Connect"); + + QGridLayout *layout = new QGridLayout(m_dialog); + layout->setSizeConstraint(QLayout::SetFixedSize); + + QLabel *srvLabel = new QLabel("Address:", m_dialog); + QLineEdit *srv = new QLineEdit(m_settings->value("address", "127.0.0.1").toString(), m_dialog); + QLabel *portLabel = new QLabel("Port:", m_dialog); + QDoubleSpinBox *port = new QDoubleSpinBox(m_dialog); + port->setDecimals(0); + port->setMinimum(1); + port->setMaximum(65535); + port->setValue(m_settings->value("port", Constants::Networking::port).toUInt()); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal, m_dialog); + QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); + QPushButton *cancelButton = buttonBox->button(QDialogButtonBox::Cancel); + buttonBox->addButton(okButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::AcceptRole)); + buttonBox->addButton(cancelButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole)); + connect(buttonBox, SIGNAL(rejected()), m_dialog, SLOT(reject())); + connect(buttonBox, SIGNAL(accepted()), m_dialog, SLOT(accept())); + + layout->addWidget(srvLabel, 0, 0); + layout->addWidget(srv, 0, 1); + layout->addWidget(portLabel, 1, 0); + layout->addWidget(port, 1, 1); + layout->addWidget(buttonBox, 4, 0, 1, 5); + + connect(m_dialog, SIGNAL(accepted()), this, SLOT(onAcceptConnectDialog())); + m_dialog->show(); +} + +void Client::onAcceptConnectDialog() +{ + if (m_dialog == NULL) + return; + QGridLayout *layout = static_cast(m_dialog->layout()); + QLineEdit *srv = static_cast(layout->itemAtPosition(0, 1)->widget()); + QDoubleSpinBox *port = static_cast(layout->itemAtPosition(1, 1)->widget()); + m_settings->setValue("address", srv->text()); + m_settings->setValue("port", int(port->value())); + m_mainWidget->doConnect(srv->text(), int(port->value())); +} + +bool Constants::server = false; + +int main(int argc, char **argv) +{ + /* Verify that the version of the library that we linked against is + * compatible with the version of the headers we compiled against. + */ + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (enet_initialize () != 0) + { + qCritical() << "An error occurred while initializing ENet"; + return EXIT_FAILURE; + } + + Q_INIT_RESOURCE(pacman); + QApplication app(argc, argv, true); + app.setOrganizationName("TU Wien FOOP"); + app.setApplicationName("Pacman Client"); + app.setWindowIcon(QIcon(":/appicon")); + + qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime())); + + AudioManager::self(); + + Client client; + client.show(); + client.setWindowTitle(app.applicationName()); + + int ret = app.exec(); + + enet_deinitialize(); + + /* Delete all global objects allocated by libprotobuf */ + google::protobuf::ShutdownProtobufLibrary(); + + return ret; +} diff --git a/pacman-c++/client/client.h b/pacman-c++/client/client.h new file mode 100644 index 0000000..9b231ca --- /dev/null +++ b/pacman-c++/client/client.h @@ -0,0 +1,41 @@ +#ifndef CLIENT_H +#define CLIENT_H + +#include "mainwidget.h" +#include + +class Client + : public QMainWindow +{ + Q_OBJECT +public: + Client(); + QSettings *settings(); + +signals: + /* signal gets emitted if mute buttons should update their checked-state */ + void setMuteActionsChecked(bool enabled); + +private slots: + /* toggles sound */ + void toggleSound(); + /* mute was changed (emitted by audioplayer/phonon) */ + void mutedChanged(bool); + /* enable ambient (emitted by action) */ + void enableAmbientSound(bool); + void showAbout(); + void showConnectDialog(); + void onAcceptConnectDialog(); + +private: + void createMenu(); + QPixmap soundIcon(bool enabled = true) const; + +private: + MainWidget *m_mainWidget; + QDialog *m_dialog; + QSettings *m_settings; + bool m_ambientMuted; +}; + +#endif // CLIENT_H diff --git a/pacman-c++/client/client.pro b/pacman-c++/client/client.pro new file mode 100644 index 0000000..849cca2 --- /dev/null +++ b/pacman-c++/client/client.pro @@ -0,0 +1,12 @@ +TEMPLATE = app +TARGET = pacman + +SOURCES += clicklabel.cpp \ + client.cpp \ + mainwidget.cpp +HEADERS += clicklabel.h \ + client.h \ + mainwidget.h + +include(../common.pri) +PRE_TARGETDEPS += ../common/libcommon.a 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; +} diff --git a/pacman-c++/client/mainwidget.h b/pacman-c++/client/mainwidget.h new file mode 100644 index 0000000..99ff7d7 --- /dev/null +++ b/pacman-c++/client/mainwidget.h @@ -0,0 +1,80 @@ +#ifndef MAINWIDGET_H +#define MAINWIDGET_H + +#include "sceneholder.h" +#include "constants.h" +#include "audio.h" +#include "pacman.pb.h" +#include +#include + +extern "C" { +#include "enet/enet.h" +} + +class Actor; + +class MainWidget + : public QWidget +{ + Q_OBJECT + +public: + MainWidget(QWidget *parent = 0); + ~MainWidget(); + bool connected(); + void setAmbientMuted(bool muted); + +public slots: + void doConnect(QString srv = "127.0.0.1", unsigned int port = Constants::Networking::port); + void doDisconnect(); + +protected: + /* handling of current key */ + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + +signals: + void connected(bool connected); + +private slots: + void startGame(); + void stopGame(); + void playerScoreClicked(); + void tick(); + void tick(ENetEvent *event); + void sendKeyUpdate(); + +private: + void createGui(); + void createMenu(); + void updateScore(const ProtoBuf::MapUpdate&); + bool isRunning(); + Color::Color connectToServer(QString srv = "127.0.0.1", unsigned int port = Constants::Networking::port); + void closeENetPeer(); + void deleteLayout(QLayout *layout); + + /* GUI elements needed in the progress of the game */ + QList m_playerScoreLayouts; + + /* key currently pressed by user */ + Transmission::field_t m_currentKey; + + /* translate Qt::Key to our key format */ + Transmission::field_t translateKey(int key); + + /* game running */ + bool m_running; + GaplessAudioPlayer *m_ambientPlayer; + + ENetHost *m_host; + ENetPeer *m_peer; + SceneHolder *m_scene; + unsigned int m_maxplayers; + QTimer *m_recvTimer; + + /* allocate as member variable as this packet is large and used often */ + ProtoBuf::MapUpdate m_updatepacket; +}; + +#endif // MAINWIDGET_H -- cgit v1.2.3