From 4f88849caf6533d965849952c9ea153fd8c518c5 Mon Sep 17 00:00:00 2001 From: totycro Date: Sat, 9 Apr 2011 19:20:13 +0200 Subject: first version of multiplayer --- pacman-c++/constants.h | 3 ++ pacman-c++/mainwidget.cpp | 82 ++++++++++++++++++++++++++++------ pacman-c++/mainwidget.h | 4 ++ pacman-c++/pacman.pro | 2 +- pacman-c++/pacman.proto | 2 + pacman-c++/pacman.server.pro | 4 +- pacman-c++/rebuild-client.sh | 4 +- pacman-c++/rebuild-server.sh | 4 +- pacman-c++/sceneholder.cpp | 3 +- pacman-c++/server.cpp | 104 ++++++++++++++++++++++++++++++++++++++----- pacman-c++/server.h | 25 +++++++++-- pacman-c++/util.cpp | 8 +++- pacman-c++/util.h | 2 + 13 files changed, 209 insertions(+), 38 deletions(-) diff --git a/pacman-c++/constants.h b/pacman-c++/constants.h index 44999f7..d81f474 100644 --- a/pacman-c++/constants.h +++ b/pacman-c++/constants.h @@ -15,6 +15,9 @@ namespace Constants { const unsigned int sprite_margin = 2; const unsigned int sprite_offset = 20; const unsigned int tick = 250; // ms + + const unsigned int port = 7321; + const unsigned int connection_timeout = 3000; } namespace Color diff --git a/pacman-c++/mainwidget.cpp b/pacman-c++/mainwidget.cpp index 06a0b9c..da08d89 100644 --- a/pacman-c++/mainwidget.cpp +++ b/pacman-c++/mainwidget.cpp @@ -4,6 +4,7 @@ #include "constants.h" #include "audioplayer.h" #include "util.h" +#include "pacman.pb.h" MainWidget::MainWidget(QWidget *parent) : SceneHolder(parent), m_currentKey(0), m_running(false) @@ -14,9 +15,15 @@ MainWidget::MainWidget(QWidget *parent) //connect(AudioPlayer::self(), SIGNAL(finished()), this, SLOT(startGame())); //AudioPlayer::self()->play(AudioPlayer::Intro); - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(tick())); - timer->start(Constants::tick); + bool connected = connectToServer(); + if (!connected) { + QMessageBox::critical(this, "Error", "Failed to connect to server, falling back to local test mode"); + // TODO: quit application here or sth + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(tick())); + timer->start(Constants::tick); + } + connect(m_socket, SIGNAL(readyRead()), this, SLOT(tick())); startGame(); } @@ -120,21 +127,49 @@ Transmission::field_t MainWidget::translateKey(int key) void MainWidget::tick() { - Actor::Movement mov = Util::transmissionMovementToActor(m_currentKey, Actor::None); +#if 0 + Actor::Movement mov = Util::transmissionMovementToActor(m_currentKey, Actor::None); - QMapIterator i(m_actors); - while (i.hasNext()) - { - i.next(); - i.value()->move(mov); - QList list(i.value()->collidingItems()); - for(int j = 0; j < list.count(); ++j) + QMapIterator i(m_actors); + while (i.hasNext()) { - if (list.at(j)->parentItem() == i.value()) - continue; - list.at(j)->setOpacity(0.6); + i.next(); + i.value()->move(mov); + QList list(i.value()->collidingItems()); + for(int j = 0; j < list.count(); ++j) + { + if (list.at(j)->parentItem() == i.value()) + continue; + list.at(j)->setOpacity(0.6); + } + } +#else + QByteArray data = m_socket->readAll(); + //qDebug() << "read qbytes " << data.length(); + // TODO: normal conversion to std::string won't work, + // probably due to \0-bytes. both those methods will yield + // a 3 char long string + //std::string dataStr = std::string(data.constData()); + //std::string dataStr = QString(data).toStdString(); + std::string dataStr; + for (int i = 0; i < data.size(); ++i) { + dataStr += data[i]; + } + + //qDebug() << "read str " << dataStr.length(); + ProtoBuf::MapUpdate packet; + packet.ParseFromString(dataStr); + Transmission::map_t map = Util::createUninitialisedMap(); + Q_ASSERT(packet.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] = packet.field(i); + ++i; } } + updateMap(map); + #endif } @@ -144,8 +179,19 @@ void MainWidget::keyPressEvent(QKeyEvent* event) return; QWidget::keyPressEvent(event); + // TODO: remove m_currentKey when we don't need it any more for testing m_currentKey = translateKey(event->key()); + // send to server + ProtoBuf::KeyPressUpdate packet; + packet.set_newkey(m_currentKey); + std::string dataStr = packet.SerializeAsString(); + unsigned int bytesWritten = m_socket->write(dataStr.c_str(), dataStr.length()); + m_socket->flush(); + Q_ASSERT(bytesWritten == dataStr.length()); + + qDebug() << "send key: " << m_currentKey; + return; // test stuff @@ -186,3 +232,11 @@ void MainWidget::playerScoreClicked() tmp->setChecked(true); return; } + +bool MainWidget::connectToServer() +{ + m_socket = new QTcpSocket(this); + m_socket->connectToHost("127.0.0.1", Constants::port); + return m_socket->waitForConnected(3000); +} + diff --git a/pacman-c++/mainwidget.h b/pacman-c++/mainwidget.h index 6adc876..eaa8be0 100644 --- a/pacman-c++/mainwidget.h +++ b/pacman-c++/mainwidget.h @@ -6,6 +6,7 @@ #include "pixmapitem.h" #include #include +#include class Actor; @@ -29,6 +30,7 @@ private: void createMenu(); void updateScore(); bool isRunning(); + bool connectToServer(); private slots: void startGame(); @@ -47,6 +49,8 @@ private: // game running bool m_running; + + QTcpSocket *m_socket; }; #endif // MAINWIDGET_H diff --git a/pacman-c++/pacman.pro b/pacman-c++/pacman.pro index 3670bc0..a346f20 100644 --- a/pacman-c++/pacman.pro +++ b/pacman-c++/pacman.pro @@ -2,7 +2,7 @@ TEMPLATE = app LANGUAGE = C++ VERSION = 0.1 -QT += phonon +QT += phonon network SOURCES += pixmapitem.cpp \ actor.cpp \ animationmanager.cpp \ diff --git a/pacman-c++/pacman.proto b/pacman-c++/pacman.proto index 39c68f7..7a582df 100644 --- a/pacman-c++/pacman.proto +++ b/pacman-c++/pacman.proto @@ -1,3 +1,5 @@ +package ProtoBuf; + message KeyPressUpdate { required uint32 newKey = 1; } diff --git a/pacman-c++/pacman.server.pro b/pacman-c++/pacman.server.pro index 7e23f21..631bc50 100644 --- a/pacman-c++/pacman.server.pro +++ b/pacman-c++/pacman.server.pro @@ -3,7 +3,9 @@ LANGUAGE = C++ VERSION = 0.1 DEFINES += SERVER -QT += phonon +TARGET = pacman-server + +QT += phonon network SOURCES += pixmapitem.cpp \ actor.cpp \ animationmanager.cpp \ diff --git a/pacman-c++/rebuild-client.sh b/pacman-c++/rebuild-client.sh index 6cbcaed..f317d6d 100755 --- a/pacman-c++/rebuild-client.sh +++ b/pacman-c++/rebuild-client.sh @@ -1,4 +1,4 @@ #!/bin/sh -make distclean +#make distclean qmake CONFIG+=DEBUG pacman.pro -make +make -j2 diff --git a/pacman-c++/rebuild-server.sh b/pacman-c++/rebuild-server.sh index b573a2e..f00d67b 100755 --- a/pacman-c++/rebuild-server.sh +++ b/pacman-c++/rebuild-server.sh @@ -1,4 +1,4 @@ #!/bin/sh -make distclean +#make distclean qmake CONFIG+=DEBUG pacman.server.pro -make +make -j2 diff --git a/pacman-c++/sceneholder.cpp b/pacman-c++/sceneholder.cpp index 0cb7d15..515d5ec 100644 --- a/pacman-c++/sceneholder.cpp +++ b/pacman-c++/sceneholder.cpp @@ -78,7 +78,8 @@ void SceneHolder::updateMap(const Transmission::map_t& map) Actor::Movement direction = Util::transmissionMovementToActor(cur & Transmission::direction_mask); - //actor->move(direction, mapPositionToCoord(x, y)); + actor->move(direction); + qDebug() << "actor move " << direction; } else { qDebug() << "abort at " << cur; diff --git a/pacman-c++/server.cpp b/pacman-c++/server.cpp index 8539410..8f9e42d 100644 --- a/pacman-c++/server.cpp +++ b/pacman-c++/server.cpp @@ -1,34 +1,39 @@ #include "server.h" +#include +#include #include "util.h" - #include "pacman.pb.h" Server::Server(QWidget *parent) : SceneHolder(parent) { + qDebug() << "waiting for clients"; + waitForClientConnections(); + qDebug() << "clients connected"; + updateMap(Util::createDummyMap()); - Transmission::map_t map = calculateUpdates(); - for (int i=0; i<10; ++i) { - qDebug() << "doing srv update"; - updateMap(map); - } + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(tick())); + timer->start(500); } -QMap< Color::Color, Actor::Movement > Server::getActorDirections() +void Server::tick() { - QMap directions; - directions[Color::red] = Actor::Down; - return directions; + qDebug() << "doing srv update"; + Transmission::map_t map = calculateUpdates(); + updateMap(map); + + QSharedPointer packet = createUpdatePacket(map); + sendUpdate(packet); } Transmission::map_t Server::calculateUpdates() { Transmission::map_t map = Util::createEmptyMap(); - QMap directions = getActorDirections(); - QMapIterator i(directions); + QMapIterator i(m_actorMovements); while (i.hasNext()) { i.next(); Actor *actor = m_actors.value( i.key() ); @@ -52,8 +57,83 @@ Transmission::map_t Server::calculateUpdates() return map; } +QSharedPointer< ProtoBuf::MapUpdate > Server::createUpdatePacket(Transmission::map_t map) +{ + QSharedPointer updatePacket = + QSharedPointer(new ProtoBuf::MapUpdate); + + for (unsigned int x = 0; x < Constants::map_size.width; ++x) { + for (unsigned int y = 0; y < Constants::map_size.height; ++y) { + updatePacket->add_field(map[x][y]); + } + } + //qDebug() << "field sz "<< updatePacket->field_size(); + return updatePacket; +} + +void Server::waitForClientConnections() +{ + QTcpServer *tcpSrv = new QTcpServer(this); + // server must stay alive as long as sockets (qt parent mem mechanism) + tcpSrv->listen(QHostAddress::Any, Constants::port); + + //for (unsigned int i=0; iwaitForNewConnection(-1); + Q_ASSERT(connectionAvailable); + QTcpSocket *socket = tcpSrv->nextPendingConnection(); + // TODO: color assignment + m_clientConnections[Color::red] = socket; + connect(socket, SIGNAL(readyRead()), this, SLOT(keyPressUpdate())); + } +} + +void Server::sendUpdate(QSharedPointer< ProtoBuf::MapUpdate > packet) +{ + std::string dataStr = packet->SerializeAsString(); + const char *data = dataStr.c_str(); + foreach(QTcpSocket *socket, m_clientConnections) { + //qDebug() << "sending str len: " << dataStr.length(); + int bytesWritten = socket->write(data, dataStr.length()); + Q_ASSERT(bytesWritten == dataStr.length()); + } + foreach(QTcpSocket *socket, m_clientConnections) { + socket->flush(); + } +} + +void Server::keyPressUpdate() +{ + qDebug() << "kpress"; + QMapIterator i(m_clientConnections); + while (i.hasNext()) { + i.next(); + Color::Color color = i.key(); + QTcpSocket *socket = i.value(); + qDebug() << "data?"; + if (socket->bytesAvailable() > 0) { + qDebug() << "data!"; + QByteArray data = socket->readAll(); + // see mainwidget.cpp:153 + std::string dataStr; + for (int i = 0; i < data.size(); ++i) { + dataStr += data[i]; + } + ProtoBuf::KeyPressUpdate packet; + packet.ParseFromString(dataStr); + Transmission::field_t direction = packet.newkey(); + qDebug() << "data:" << direction; + m_actorMovements[ color ] = Util::transmissionMovementToActor(direction); + } + } + +} + + int main(int argc, char ** argv) { + GOOGLE_PROTOBUF_VERIFY_VERSION; + QApplication app(argc, argv); app.setApplicationName("Pacman Server"); app.setWindowIcon(QIcon(":/appicon")); diff --git a/pacman-c++/server.h b/pacman-c++/server.h index 006b104..6ffbb35 100644 --- a/pacman-c++/server.h +++ b/pacman-c++/server.h @@ -5,6 +5,9 @@ #include #include "actor.h" +#include "pacman.pb.h" + +class QTcpSocket; class Server : public SceneHolder @@ -13,13 +16,29 @@ class Server public: Server(QWidget *parent = 0); +protected slots: + void tick(); + + // receive updates of client + void keyPressUpdate(); + protected: - // returns packets read from network until now - // for clients not sending a direction, no change is assumed - QMap getActorDirections(); + + // block until we have connections from all clients + void waitForClientConnections(); // calculate updates of current tick for sending to client Transmission::map_t calculateUpdates(); + + QSharedPointer createUpdatePacket(Transmission::map_t); + + // update client maps + void sendUpdate(QSharedPointer< ProtoBuf::MapUpdate > ); + + QMap m_clientConnections; + + // current movements. required to make pacmans continue their movement. + QMap m_actorMovements; }; #endif // SERVER_H diff --git a/pacman-c++/util.cpp b/pacman-c++/util.cpp index 99a643e..6b9780b 100644 --- a/pacman-c++/util.cpp +++ b/pacman-c++/util.cpp @@ -2,12 +2,16 @@ namespace Util { - Transmission::map_t createEmptyMap() { - Transmission::map_t map; + Transmission::map_t createUninitialisedMap() { + Transmission::map_t map; map = new Transmission::field_t*[Constants::map_size.width]; for (unsigned int i = 0; i < Constants::map_size.width; ++i) map[i] = new Transmission::field_t[Constants::map_size.height]; + return map; + } + Transmission::map_t createEmptyMap() { + Transmission::map_t map = createUninitialisedMap(); for (unsigned int x = 0; x < Constants::map_size.width; ++x) { for (unsigned int y = 0; y < Constants::map_size.height; ++y) diff --git a/pacman-c++/util.h b/pacman-c++/util.h index 1754f54..8bd03bc 100644 --- a/pacman-c++/util.h +++ b/pacman-c++/util.h @@ -5,6 +5,8 @@ #include "actor.h" namespace Util { + Transmission::map_t createUninitialisedMap(); + Transmission::map_t createDummyMap(); Transmission::map_t createEmptyMap(); -- cgit v1.2.3