diff options
Diffstat (limited to 'chicken-wranglers/src/main/gameclient.cpp')
-rw-r--r-- | chicken-wranglers/src/main/gameclient.cpp | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/chicken-wranglers/src/main/gameclient.cpp b/chicken-wranglers/src/main/gameclient.cpp new file mode 100644 index 0000000..4d2e461 --- /dev/null +++ b/chicken-wranglers/src/main/gameclient.cpp @@ -0,0 +1,423 @@ +/**************************************************************************** +** +** This file is a part of QtChickenWranglers. +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).* +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include <QtCore/QStringList> +#include <QtDeclarative/QDeclarativeContext> + +#include "charactermodel.h" +#include "connectivitylistmodel.h" +#include "control.h" +#include "global.h" +#include "networkclient.h" +#include "sensormovement.h" +#include "settings.h" + +#include "gameclient.h" + +GameClient::GameClient(QObject *parent) + : QObject(parent) +{ +} + +GameClient::GameClient(QDeclarativeContext *context, const QString &connectivity, QObject *parent) + : QObject(parent), m_declarativeContext(context), m_networkClient(0), + m_characterListModel(0), m_connectivityListModel(0), m_control(new Control(this)), + m_status(NoStatus), m_error(NoError) +{ + m_networkClient = new NetworkClient(connectivity, Settings::lanUdpDiscoveryTimeout(), + Settings::lanUdpDiscoveryClientAttempts(), this); + m_networkClient->setLanPorts(Settings::lanUdpPort(), Settings::lanTcpPort()); + + connect(m_networkClient, SIGNAL(serverDiscoveryTimeout()), + this, SLOT(onServerDiscoveryTimeout())); + connect(m_networkClient, SIGNAL(connected()), + this, SLOT(onConnected())); + connect(m_networkClient, SIGNAL(disconnected()), + this, SLOT(onDisconnected())); + connect(m_networkClient, SIGNAL(socketError(QAbstractSocket::SocketError)), + this, SLOT(onSocketError(QAbstractSocket::SocketError))); + connect(m_networkClient, SIGNAL(messageReceived(NetworkMessage)), + this, SLOT(onMessageReceived(NetworkMessage))); + + m_connectivityListModel = new ConnectivityListModel(this); + connect(m_networkClient, SIGNAL(bluetoothDeviceDiscovered(const BluetoothDevice &)), + m_connectivityListModel, SLOT(addDevice(const BluetoothDevice &))); + connect(m_connectivityListModel, SIGNAL(selectedDevice(const QString &)), + m_networkClient, SLOT(stopDiscovery())); + connect(m_connectivityListModel, SIGNAL(selectedDevice(const QString &)), + m_networkClient, SLOT(connectToBluetoothServer(const QString &))); + m_declarativeContext->setContextProperty("connectivityListModel", m_connectivityListModel); + + SensorMovement *sensorMovement = m_control->sensorMovement(); + sensorMovement->setMinimumRotation(1); + sensorMovement->setThreshold(0.2); + + connect(m_control, SIGNAL(directionChanged(Global::Direction)), + this, SLOT(onControlDirectionChanged(Global::Direction))); + + // TODO: This was tested only on Maemo5. There should + // be specific implementations for Maemo6 and Symbian + // + // m_backlightTimer.setInterval(20000); + // connect(&m_backlightTimer, SIGNAL(timeout()), this, SLOT(forceBackLightOn())); +} + +void GameClient::setStatus(GameClient::Status status) +{ + if (m_status == status) + return; + + m_status = status; + + emit statusChanged(); +} + +void GameClient::setLeader(bool isLeader) +{ + if (m_isLeader == isLeader) + return; + + m_isLeader = isLeader; + + emit isLeaderChanged(); +} + +void GameClient::startConnection() +{ + m_error = NoError; + m_status = NoStatus; + + m_connectivityListModel->loadLastHostDevice(); + m_networkClient->startDiscovery(); +} + +void GameClient::toggleLaserUp() +{ + notifyPlayerControlLaser(CW::PlayerControlLaserUp); +} + +void GameClient::toggleLaserDown() +{ + notifyPlayerControlLaser(CW::PlayerControlLaserDown); +} + +void GameClient::toggleLaserLeft() +{ + notifyPlayerControlLaser(CW::PlayerControlLaserLeft); +} + +void GameClient::toggleLaserRight() +{ + notifyPlayerControlLaser(CW::PlayerControlLaserRight); +} + +void GameClient::moveUp() +{ + onControlDirectionChanged(Global::DirectionUp); +} + +void GameClient::moveDown() +{ + onControlDirectionChanged(Global::DirectionDown); +} + +void GameClient::moveLeft() +{ + onControlDirectionChanged(Global::DirectionLeft); +} + +void GameClient::moveRight() +{ + onControlDirectionChanged(Global::DirectionRight); +} + +void GameClient::moveStop() +{ + onControlDirectionChanged(Global::DirectionStop); +} + +void GameClient::quit() +{ + m_error = NoError; + m_status = NoStatus; + + m_networkClient->stopDiscovery(); + + emit ended(); +} + +void GameClient::onServerDiscoveryTimeout() +{ + m_error = ServerDiscoveryTimeoutError; + + setStatus(Error); + + quit(); +} + +void GameClient::onConnected() +{ + m_connectivityListModel->storeLastHostDevice(); + + disconnect(m_networkClient, SIGNAL(connected()), this, SLOT(onConnected())); + + loadCharacters(); + + setStatus(Joined); +} + +void GameClient::onDisconnected() +{ + disconnect(m_networkClient, SIGNAL(disconnected()), this, SLOT(onDisconnected())); + + setStatus(NoStatus); +} + +void GameClient::onMessageReceived(const NetworkMessage &message) +{ + switch (message.type()) { + case CW::CharacterMessageType: + handleCharacterMessage(message); + break; + case CW::MatchMessageType: + handleMatchMessage(message); + break; + case CW::PlayerInfoMessageType: + handlePlayerInfoMessage(message); + break; + default: + qWarning("Invalid message type"); + } +} + +void GameClient::onSocketError(QAbstractSocket::SocketError error) +{ + Q_UNUSED(error); + + m_error = SocketError; + + setStatus(Error); +} + +void GameClient::loadCharacters() +{ + if (!m_characterListModel) + m_characterListModel = new CharacterListModel(this); + + if (!m_characterListModel->load()) {} + + m_declarativeContext->setContextProperty("characterListModel", m_characterListModel); + + connect(m_characterListModel, SIGNAL(selectedIndexChanged()), + this, SLOT(onModelSelectedIndexChanged())); +} + +void GameClient::onModelSelectedIndexChanged() +{ + int selectedIndex = m_characterListModel->selectedIndex(); + + if (selectedIndex != -1) { + CharacterModel *characterModel = m_characterListModel->get(selectedIndex); + notifyCharacterSelected(characterModel->name()); + } else { + notifyCharacterReleased(); + } +} + +void GameClient::notifyCharacterSelected(const QString &characterName) +{ + NetworkMessage message(CW::CharacterMessageType, CW::CharacterSelected); + message.setData(characterName.toLatin1()); + + m_networkClient->send(message); +} + +void GameClient::notifyCharacterReleased() +{ + NetworkMessage message(CW::CharacterMessageType, CW::CharacterReleased); + + m_networkClient->send(message); +} + +void GameClient::notifyPlayerControlLaser(CW::PlayerControl laserDirection) +{ + NetworkMessage message(CW::PlayerControlMessageType, laserDirection); + + m_networkClient->send(message); +} + +void GameClient::handleCharacterMessage(const NetworkMessage &message) +{ + if (!m_characterListModel) { + // XXX: Properly thrown an exception for the Game class + qCritical("Error: CharacterListModel not initialized."); + return; + } + + switch (message.content()) { + case CW::CharacterSelected: { + QString characterName(message.data()); + + CharacterModel *model; + for (int i = 0; i < m_characterListModel->rowCount(); i++) { + model = m_characterListModel->get(i); + if (model->name() == characterName) + model->setAvailable(false); + } + + break; + } + case CW::CharacterAvailableList: { + QStringList availableCharacters(QString(message.data()).split("|")); + + CharacterModel *model; + for (int i = 0; i < m_characterListModel->rowCount(); i++) { + model = m_characterListModel->get(i); + if (!availableCharacters.contains(model->name())) + model->setAvailable(false); + } + + break; + } + case CW::CharacterIsAvailable: { + QStringList messageDataList(QString(message.data()).split("|")); + QString characterName = messageDataList.at(0); + + bool characterIsAvailable(true); + if (messageDataList.at(1) == "0") + characterIsAvailable = false; + + CharacterModel *model; + for (int i = 0; i < m_characterListModel->rowCount(); i++) { + model = m_characterListModel->get(i); + + if (model->name() == characterName) + model->setAvailable(characterIsAvailable); + } + + break; + } + } +} + +void GameClient::handleMatchMessage(const NetworkMessage &message) +{ + switch (message.content()) { + case CW::MatchReady: + setStatus(Ready); + m_control->start(); + forceBackLightOn(); + m_backlightTimer.start(); + break; + case CW::MatchFinished: + setStatus(LeftMatch); + m_control->stop(); + m_backlightTimer.stop(); + break; + default: + qWarning("Invalid match message"); + } +} + +void GameClient::handlePlayerInfoMessage(const NetworkMessage &message) +{ + switch (message.content()) { + case CW::PlayerInfoLeader: { + bool isLeader(false); + if (message.data() == "1") + isLeader = true; + + setLeader(isLeader); + + break; + } + default: + qWarning("Invalid player info message"); + } +} + +// FIXME: Move this function to a better place +static CW::PlayerControl directionToPlayerControl(Global::Direction direction) +{ + QMap<Global::Direction, CW::PlayerControl> playerControl; + + playerControl[Global::DirectionUp] = CW::PlayerControlUp; + playerControl[Global::DirectionDown] = CW::PlayerControlDown; + playerControl[Global::DirectionRight] = CW::PlayerControlRight; + playerControl[Global::DirectionLeft] = CW::PlayerControlLeft; + playerControl[Global::DirectionStop] = CW::PlayerControlStop; + + return playerControl[direction]; +} + +void GameClient::onControlDirectionChanged(Global::Direction direction) +{ + NetworkMessage message(CW::PlayerControlMessageType, directionToPlayerControl(direction)); + + m_networkClient->send(message); +} + +void GameClient::forceBackLightOn() +{ + /* TODO: Provide specific implementations for Maemo6 and Symbian + + if (!QDBusConnection::systemBus().isConnected()) { + qWarning("Error connecting to session bus"); + return; + } + + QDBusInterface iface("com.nokia.mce", "/com/nokia/mce/request", "com.nokia.mce.request", + QDBusConnection::systemBus()); + + if (iface.isValid()) + iface.call("req_tklock_mode_change", QString("unlocked")); + else + qWarning("Error calling com.nokia.mce.request.req_tklock_mode_change"); + */ +} + +void GameClient::scanCanceled() +{ + m_networkClient->stopDiscovery(); + + // scan is assynchronous, so wait a little bit before cleaning the model + QTimer::singleShot(300, m_connectivityListModel, SLOT(clear())); +} |