3
0
Fork 0
mirror of https://github.com/lubosz/overte.git synced 2025-04-27 17:55:27 +02:00

Allow logged-in users to name their current location & orientation

This commit is contained in:
stojce 2014-02-08 01:10:26 +01:00
parent 98967189ac
commit bbdf4a1205
12 changed files with 410 additions and 6 deletions

View file

@ -20,6 +20,11 @@ const quint16 DATA_SERVER_LISTEN_PORT = 3282;
const char REDIS_HOSTNAME[] = "127.0.0.1";
const unsigned short REDIS_PORT = 6379;
const int ARGV_FIXED_INDEX_START = 2;
const char REDIS_HASH_MULTIPLE_SET[] = "HMSET";
const char REDIS_HASH_SET[] = "HSET";
const char REDIS_HASH_GET_ALL[] = "HGETALL";
DataServer::DataServer(int argc, char* argv[]) :
QCoreApplication(argc, argv),
@ -65,6 +70,71 @@ void DataServer::readPendingDatagrams() {
PacketType requestType = packetTypeForPacket(receivedPacket);
if ((requestType == PacketTypeDataServerHashPut || requestType == PacketTypeDataServerHashGet) &&
packetVersionMatch(receivedPacket)) {
QDataStream packetStream(receivedPacket);
int numReceivedHeaderBytes = numBytesForPacketHeader(receivedPacket);
packetStream.skipRawData(numReceivedHeaderBytes);
// pull the sequence number used for this packet
quint8 sequenceNumber = 0;
packetStream >> sequenceNumber;
// pull the UUID that we will need as part of the key
QString userString;
packetStream >> userString;
if (requestType == PacketTypeDataServerHashPut) {
QString dataKey, dataValue;
QStringList redisCommandKeys, redisCommandValues;
while(true) {
packetStream >> dataKey >> dataValue;
if (dataKey.isNull() || dataKey.isEmpty()) {
break;
}
redisAppendCommand(_redis, "%s %s %s %s",
REDIS_HASH_SET,
qPrintable(userString),
qPrintable(dataKey),
qPrintable(dataValue));
};
redisReply* reply = NULL;
redisGetReply(_redis, (void **) &reply);
if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) {
QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerConfirm, _uuid);
replyPacket.append(sequenceNumber);
_socket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort());
}
freeReplyObject(reply);
reply = NULL;
} else {
redisReply* reply = (redisReply*) redisCommand(_redis, "%s %s", REDIS_HASH_GET_ALL, qPrintable(userString));
QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerHashSend, _uuid);
QDataStream sendPacketStream(&sendPacket, QIODevice::Append);
sendPacketStream << sequenceNumber;
sendPacketStream << userString;
if (reply->type == REDIS_REPLY_ARRAY && reply->elements > 0) {
for (int i = 0; i < reply->elements; i++) {
sendPacketStream << QString(reply->element[i]->str);
}
}
// reply back with the send packet
_socket.writeDatagram(sendPacket, senderSockAddr.getAddress(), senderSockAddr.getPort());
freeReplyObject(reply);
reply = NULL;
}
}
if ((requestType == PacketTypeDataServerPut || requestType == PacketTypeDataServerGet) &&
packetVersionMatch(receivedPacket)) {

View file

@ -57,7 +57,7 @@ configure_file(InterfaceVersion.h.in ${PROJECT_BINARY_DIR}/includes/InterfaceVer
# grab the implementation and header files from src dirs
file(GLOB INTERFACE_SRCS src/*.cpp src/*.h)
foreach(SUBDIR avatar devices renderer ui starfield)
foreach(SUBDIR avatar devices renderer ui starfield location)
file(GLOB_RECURSE SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h)
set(INTERFACE_SRCS ${INTERFACE_SRCS} ${SUBDIR_SRCS})
endforeach(SUBDIR)

View file

@ -116,7 +116,10 @@ void DatagramProcessor::processDatagrams() {
}
case PacketTypeDataServerGet:
case PacketTypeDataServerPut:
case PacketTypeDataServerHashPut:
case PacketTypeDataServerHashGet:
case PacketTypeDataServerSend:
case PacketTypeDataServerHashSend:
case PacketTypeDataServerConfirm:
DataServerClient::processMessageFromDataServer(incomingPacket);
break;

View file

@ -23,6 +23,7 @@
#include <QStandardPaths>
#include <QUuid>
#include <QWindow>
#include <QMessageBox>
#include <UUID.h>
@ -114,7 +115,12 @@ Menu::Menu() :
MenuOption::GoToLocation,
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
this,
SLOT(goToLocation()));
SLOT(goToLocation()));
addActionToQMenuAndActionHash(fileMenu,
MenuOption::NameLocation,
Qt::CTRL | Qt::Key_N,
this,
SLOT(nameLocation()));
addActionToQMenuAndActionHash(fileMenu,
MenuOption::GoTo,
Qt::Key_At,
@ -152,8 +158,9 @@ Menu::Menu() :
addActionToQMenuAndActionHash(editMenu, MenuOption::CutVoxels, Qt::CTRL | Qt::Key_X, appInstance, SLOT(cutVoxels()));
addActionToQMenuAndActionHash(editMenu, MenuOption::CopyVoxels, Qt::CTRL | Qt::Key_C, appInstance, SLOT(copyVoxels()));
addActionToQMenuAndActionHash(editMenu, MenuOption::PasteVoxels, Qt::CTRL | Qt::Key_V, appInstance, SLOT(pasteVoxels()));
addActionToQMenuAndActionHash(editMenu, MenuOption::NudgeVoxels, Qt::CTRL | Qt::Key_N, appInstance, SLOT(nudgeVoxels()));
// TODO: add new shortcut
addActionToQMenuAndActionHash(editMenu, MenuOption::NudgeVoxels, 0, appInstance, SLOT(nudgeVoxels()));
#ifdef __APPLE__
addActionToQMenuAndActionHash(editMenu, MenuOption::DeleteVoxels, Qt::Key_Backspace, appInstance, SLOT(deleteVoxels()));
#else
@ -964,6 +971,70 @@ void Menu::goTo() {
sendFakeEnterEvent();
}
void Menu::namedLocationCreated(LocationManager::LocationCreateResponse response, NamedLocation* location) {
if (response == LocationManager::AlreadyExists) {
QMessageBox msgBox;
msgBox.setText("That name has been claimed by " + location->getCreatedBy() + ", try something else.");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.button(QMessageBox::Ok)->setText("Go to user");
int ret = msgBox.exec();
if (ret == QMessageBox::Ok) {
DataServerClient::getValuesForKeysAndUserString(
QStringList()
<< DataServerKey::Domain
<< DataServerKey::Position
<< DataServerKey::Orientation,
location->getCreatedBy(),
Application::getInstance()->getProfile());
}
}
}
void Menu::nameLocation() {
// check if user is logged in or show login dialog if not
Profile* profile = Application::getInstance()->getProfile();
if (profile->getUsername().isNull()) {
QMessageBox msgBox;
msgBox.setText("We need to tie this location to your username.");
msgBox.setInformativeText("Please login first, then try naming the location again.");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.button(QMessageBox::Ok)->setText("Login");
if (msgBox.exec() == QMessageBox::Ok) {
login();
}
return;
}
QInputDialog nameDialog(Application::getInstance()->getWindow());
nameDialog.setWindowTitle("Name this location");
nameDialog.setLabelText("Name this location, then share that name with others.\n"
"When they come here, they'll be in the same location and orientation\n"
"(wherever you are standing and looking now) as you.\n\n"
"Location name:");
nameDialog.setWindowFlags(Qt::Sheet);
nameDialog.resize((int) (nameDialog.parentWidget()->size().width() * 0.30), nameDialog.size().height());
if (nameDialog.exec() == QDialog::Accepted) {
QString locationName = nameDialog.textValue().trimmed();
if (locationName.isEmpty()) {
return;
}
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
LocationManager* manager = new LocationManager();
connect(manager,
SIGNAL(creationCompleted(LocationManager::LocationCreateResponse, NamedLocation*)),
SLOT(namedLocationCreated(LocationManager::LocationCreateResponse, NamedLocation*)));
manager->createNamedLocation(locationName, profile->getUsername(), myAvatar->getPosition(), myAvatar->getOrientation());
}
}
void Menu::goToLocation() {
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();

View file

@ -15,6 +15,7 @@
#include <QPointer>
#include <AbstractMenuInterface.h>
#include "location/LocationManager.h"
enum FrustumDrawMode {
FRUSTUM_DRAW_MODE_ALL,
@ -101,6 +102,7 @@ private slots:
void editPreferences();
void goToDomain();
void goToLocation();
void nameLocation();
void bandwidthDetailsClosed();
void voxelStatsDetailsClosed();
void lodToolsClosed();
@ -111,6 +113,7 @@ private slots:
void resetSwatchColors();
void showMetavoxelEditor();
void audioMuteToggled();
void namedLocationCreated(LocationManager::LocationCreateResponse response, NamedLocation* location);
private:
static Menu* _instance;
@ -210,6 +213,7 @@ namespace MenuOption {
const QString GlowMode = "Cycle Glow Mode";
const QString GoToDomain = "Go To Domain...";
const QString GoToLocation = "Go To Location...";
const QString NameLocation = "Name this location";
const QString GoTo = "Go To...";
const QString ImportVoxels = "Import Voxels";
const QString ImportVoxelsClipboard = "Import Voxels to Clipboard";

View file

@ -0,0 +1,26 @@
//
// LocationManager.cpp
// hifi
//
// Created by Stojce Slavkovski on 2/7/14.
//
//
#include "LocationManager.h"
void LocationManager::createNamedLocation(QString locationName, QString creator, glm::vec3 location, glm::quat orientation) {
_namedLocation = new NamedLocation(locationName, creator, location, orientation);
connect(_namedLocation, SIGNAL(dataReceived(bool)), SLOT(locationDataReceived(bool)));
DataServerClient::getHashFieldsForKey(DataServerKey::NamedLocation, _namedLocation->locationName(), _namedLocation);
}
void LocationManager::locationDataReceived(bool locationExists) {
disconnect(_namedLocation, SIGNAL(dataReceived(bool)), this, SLOT(locationDataReceived(bool)));
if (locationExists) {
emit creationCompleted(AlreadyExists, _namedLocation);
} else {
DataServerClient::putHashFieldsForKey(DataServerKey::NamedLocation, _namedLocation->locationName(), _namedLocation);
emit creationCompleted(Created, _namedLocation);
}
}

View file

@ -0,0 +1,37 @@
//
// LocationManager.h
// hifi
//
// Created by Stojce Slavkovski on 2/7/14.
//
//
#ifndef __hifi__LocationManager__
#define __hifi__LocationManager__
#include <QtCore>
#include "NamedLocation.h"
class LocationManager : public QObject {
Q_OBJECT
public:
enum LocationCreateResponse {
Created,
AlreadyExists
};
LocationManager() { };
void createNamedLocation(QString locationName, QString creator, glm::vec3 location, glm::quat orientation);
signals:
void creationCompleted(LocationManager::LocationCreateResponse response, NamedLocation* location);
private:
NamedLocation* _namedLocation;
private slots:
void locationDataReceived(bool locationExists);
};
#endif /* defined(__hifi__LocationManager__) */

View file

@ -0,0 +1,46 @@
//
// LocationManager.cpp
// hifi
//
// Created by Stojce Slavkovski on 2/1/14.
//
//
#include "NamedLocation.h"
// deserialize data
void NamedLocation::processDataServerResponse(const QString& userString,
const QStringList& keyList,
const QStringList& valueList) {
for (int i = 0; i < keyList.count(); i++) {
if (keyList[i] == "creator") {
_createdBy = valueList[i];
} else if (keyList[i] == "location") {
QStringList locationCoords = valueList[i].split(",");
if (locationCoords.length() == 3) {
_location = glm::vec3(locationCoords[0].toLong(), locationCoords[1].toLong(), locationCoords[2].toLong());
}
} else if (keyList[i] == "orientation") {
QStringList orientationCoords = valueList[i].split(",");
if (orientationCoords.length() == 4) {
_orientation = glm::quat(orientationCoords[0].toLong(),
orientationCoords[1].toLong(),
orientationCoords[2].toLong(),
orientationCoords[3].toLong());
}
}
}
emit dataReceived(keyList.count() > 0);
}
// serialize data
QHash<QString, QString> NamedLocation::getHashData() {
QHash<QString, QString> response;
qDebug() << QString::fromStdString(glm::to_string(_location));
response["location"] = QString::number(_location.x) + "," + QString::number(_location.y) + "," + QString::number(_location.z);
response["creator"] = _createdBy;
response["orientation"] = QString::number(_orientation.x) + "," + QString::number(_orientation.y) + "," + QString::number(_orientation.z) + "," + QString::number(_orientation.w);
return response;
}

View file

@ -0,0 +1,63 @@
//
// NamedLocation.h
// hifi
//
// Created by Stojce Slavkovski on 2/1/14.
//
//
#ifndef __hifi__NamedLocation__
#define __hifi__NamedLocation__
#include <glm/glm.hpp>
#include <glm/gtx/string_cast.hpp>
#include <glm/gtc/quaternion.hpp>
#include "DataServerClient.h"
class NamedLocation : public QObject, public DataServerCallbackObject, public DataServerCallerObject {
Q_OBJECT
public:
NamedLocation(QString locationName, QString createdBy, glm::vec3 location, glm::quat orientation) {
_locationName = locationName;
_createdBy = createdBy;
_location = location;
_orientation = orientation;
}
bool isEmpty() { return _locationName.isNull() || _locationName.isEmpty(); }
// DataServerCallbackObject implementation
void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList);
// DataServerCallerObject implementation
QHash<QString, QString> getHashData();
// properties >>
void setLocationName(QString locationName) { _locationName = locationName; }
QString locationName() { return _locationName; }
void createdBy(QString createdBy) { _createdBy = createdBy; }
QString getCreatedBy() { return _createdBy; }
void setLocation(glm::vec3 location) { _location = location; }
glm::vec3 location() { return _location; }
void setOrientation(glm::quat orentation) { _orientation = orentation; }
glm::quat orientation() { return _orientation; }
// properties <<
signals:
void dataReceived(bool locationExists);
private:
QString _locationName;
QString _createdBy;
glm::vec3 _location;
glm::quat _orientation;
};
#endif /* defined(__hifi__NamedLocation__) */

View file

@ -88,6 +88,44 @@ void DataServerClient::getValueForKeyAndUserString(const QString& key, const QSt
getValuesForKeysAndUserString(QStringList(key), userString, callbackObject);
}
void DataServerClient::getHashFieldsForKey(const QString serverKey, QString keyValue, DataServerCallbackObject* callbackObject) {
QByteArray getPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerHashGet);
QDataStream packetStream(&getPacket, QIODevice::Append);
packetStream << _sequenceNumber << serverKey + ":" + keyValue;
// add the getPacket to our map of unconfirmed packets, will be deleted once we get a response from the nameserver
_unmatchedPackets.insert(_sequenceNumber, getPacket);
_callbackObjects.insert(_sequenceNumber, callbackObject);
// send the get to the data server
NodeList::getInstance()->getNodeSocket().writeDatagram(getPacket, dataServerSockAddr().getAddress(),
dataServerSockAddr().getPort());
_sequenceNumber++;
}
void DataServerClient::putHashFieldsForKey(const QString serverKey, QString keyValue, DataServerCallerObject* callerObject) {
QByteArray putPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerHashPut);
QDataStream packetStream(&putPacket, QIODevice::Append);
packetStream << _sequenceNumber << serverKey + ":" + keyValue;
QHash<QString, QString> hashData(callerObject->getHashData());
QHash<QString, QString>::const_iterator i = hashData.constBegin();
while (i != hashData.constEnd()) {
packetStream << i.key() << i.value();
++i;
}
// add the getPacket to our map of unconfirmed packets, will be deleted once we get a response from the nameserver
_unmatchedPackets.insert(_sequenceNumber, putPacket);
// send this put request to the data server
NodeList::getInstance()->getNodeSocket().writeDatagram(putPacket, dataServerSockAddr().getAddress(),
dataServerSockAddr().getPort());
_sequenceNumber++;
}
void DataServerClient::processConfirmFromDataServer(const QByteArray& packet) {
removeMatchedPacketFromMap(packet);
}
@ -113,12 +151,47 @@ void DataServerClient::processSendFromDataServer(const QByteArray& packet) {
valueListString.split(MULTI_KEY_VALUE_SEPARATOR));
}
}
void DataServerClient::processHashSendFromDataServer(const QByteArray& packet) {
// pull the user string from the packet so we know who to associate this with
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
quint8 sequenceNumber = 0;
packetStream >> sequenceNumber;
if (_callbackObjects.find(sequenceNumber) != _callbackObjects.end()) {
// remove the packet from our two maps, it's matched
DataServerCallbackObject* callbackObject = _callbackObjects.take(sequenceNumber);
_unmatchedPackets.remove(sequenceNumber);
QString userString, keyString, valueString;
QStringList keyList, valueList;
packetStream >> userString;
while(true) {
packetStream >> keyString;
packetStream >> valueString;
if (keyString.isNull() || keyString.isEmpty()) {
break;
}
keyList << keyString;
valueList << valueString;
}
callbackObject->processDataServerResponse(userString, keyList, valueList);
}
}
void DataServerClient::processMessageFromDataServer(const QByteArray& packet) {
switch (packetTypeForPacket(packet)) {
case PacketTypeDataServerSend:
processSendFromDataServer(packet);
break;
case PacketTypeDataServerHashSend:
processHashSendFromDataServer(packet);
break;
case PacketTypeDataServerConfirm:
processConfirmFromDataServer(packet);
break;

View file

@ -18,6 +18,11 @@
#include "HifiSockAddr.h"
class DataServerCallerObject {
public:
virtual QHash<QString, QString> getHashData() = 0;
};
class DataServerCallbackObject {
public:
virtual void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList) = 0;
@ -35,14 +40,16 @@ public:
static void getValueForKeyAndUUID(const QString& key, const QUuid& uuid, DataServerCallbackObject* callbackObject);
static void getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, DataServerCallbackObject* callbackObject);
static void getValuesForKeysAndUserString(const QStringList& keys, const QString& userString,
DataServerCallbackObject* callbackObject);
DataServerCallbackObject* callbackObject);
static void getHashFieldsForKey(const QString serverKey, QString keyValue, DataServerCallbackObject* callbackObject);
static void putHashFieldsForKey(const QString serverKey, QString keyValue, DataServerCallerObject* callerObject);
static void processMessageFromDataServer(const QByteArray& packet);
static void resendUnmatchedPackets();
private:
static void processConfirmFromDataServer(const QByteArray& packet);
static void processSendFromDataServer(const QByteArray& packet);
static void processHashSendFromDataServer(const QByteArray& packet);
static void removeMatchedPacketFromMap(const QByteArray& packet);
static QMap<quint8, QByteArray> _unmatchedPackets;
@ -57,6 +64,7 @@ namespace DataServerKey {
const QString Position = "position";
const QString Orientation = "orientation";
const QString UUID = "uuid";
const QString NamedLocation = "namedlocation";
}
#endif /* defined(__hifi__DataServerClient__) */

View file

@ -36,6 +36,9 @@ enum PacketType {
PacketTypeCreateAssignment,
PacketTypeDataServerPut,
PacketTypeDataServerGet,
PacketTypeDataServerHashPut,
PacketTypeDataServerHashGet,
PacketTypeDataServerHashSend,
PacketTypeDataServerSend,
PacketTypeDataServerConfirm,
PacketTypeVoxelQuery,