Merge branch 'master' of git://github.com/highfidelity/hifi into entity-tools-polish-2

This commit is contained in:
Ryan Huffman 2014-12-03 08:40:38 -08:00
commit 5c5c66b265
16 changed files with 177 additions and 160 deletions

View file

@ -96,12 +96,13 @@ Currently building on Windows has been tested using the following compilers:
#####Windows SDK 7.1
Whichever version of Visual Studio you use, you will need [Microsoft Windows SDK for Windows 7 and .NET Framework 4](http://www.microsoft.com/en-us/download/details.aspx?id=8279).
If using Visual Studio 2010, or using Visual Studio 2013 but building as a Visual Studio 2010 project, you need [Microsoft Windows SDK for Windows 7 and .NET Framework 4](http://www.microsoft.com/en-us/download/details.aspx?id=8279).
NOTE: If using Visual Studio C++ 2010 Express, you need to follow a specific install order. See below before installing the Windows SDK.
######Windows 8.1
You may have already downloaded the Windows 8 SDK (e.g. if you have previously installed Visual Studio 2013). If so, change CMAKE_PREFIX_PATH in %HIFI_DIR%\CMakeLists.txt to point to the Windows 8 SDK binaries. The default path is `C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86`
######Windows SDK 8.1
If using Visual Studio 2013 and building as a Visual Studio 2013 project you need the Windows 8 SDK which you should already have as part of installing Visual Studio 2013. You should be able to see it at `C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86`.
#####Visual Studio C++ 2010 Express
@ -123,9 +124,11 @@ Some of the build instructions will ask you to start a Visual Studio Command Pro
#####Visual Studio 2013
This product must be purchased separately.
You can use the Community or Professional editions of Visual Studio 2013.
Visual Studio 2013 doesn't have a shortcut to start a Visual Studio Command Prompt. Instead, start a regular command prompt and then run:
You can start a Visual Studio 2013 command prompt using the shortcut provided in the Visual Studio Tools folder installed as part of Visual Studio 2013.
Or you can start a regular command prompt and then run:
"%VS120COMNTOOLS%\vsvars32.bat"
@ -146,6 +149,8 @@ Once Qt is installed, you need to manually configure the following:
* Make sure the Qt runtime DLLs are loadable. You must do this before you attempt to build because some tools for the build depend on Qt. E.g., add to the PATH: `Qt\5.2.0\msvc2010_opengl\bin\`.
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.2.0\msvc2010_opengl` directory.
If building as a Visual Studio 2013 project, download and configure the msvc2013 version of Qt instead.
####External Libraries
CMake will need to know where the headers and libraries for required external dependencies are.

View file

@ -48,8 +48,13 @@ elseif (UNIX)
select_library_configurations(XINERAMA)
elseif (WIN32)
find_library(LIBOVR_LIBRARY_DEBUG NAMES libovrd PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${LIBOVR_SEARCH_DIRS})
find_library(LIBOVR_LIBRARY_RELEASE NAMES libovr PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${LIBOVR_SEARCH_DIRS})
if (MSVC10)
find_library(LIBOVR_LIBRARY_DEBUG NAMES libovrd PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${LIBOVR_SEARCH_DIRS})
find_library(LIBOVR_LIBRARY_RELEASE NAMES libovr PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${LIBOVR_SEARCH_DIRS})
elseif (MSVC12)
find_library(LIBOVR_LIBRARY_DEBUG NAMES libovrd PATH_SUFFIXES Lib/Win32/VS2013 HINTS ${LIBOVR_SEARCH_DIRS})
find_library(LIBOVR_LIBRARY_RELEASE NAMES libovr PATH_SUFFIXES Lib/Win32/VS2013 HINTS ${LIBOVR_SEARCH_DIRS})
endif ()
find_package(ATL)
endif ()

View file

@ -37,6 +37,8 @@
int const DomainServer::EXIT_CODE_REBOOT = 234923;
const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io";
DomainServer::DomainServer(int argc, char* argv[]) :
QCoreApplication(argc, argv),
_shutdownEventListener(this),
@ -52,7 +54,8 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_webAuthenticationStateSet(),
_cookieSessionHash(),
_automaticNetworkingSetting(),
_settingsManager()
_settingsManager(),
_iceServerSocket(ICE_SERVER_DEFAULT_HOSTNAME, ICE_SERVER_DEFAULT_PORT)
{
LogUtils::init();
@ -337,57 +340,67 @@ bool DomainServer::optionallySetupAssignmentPayment() {
void DomainServer::setupAutomaticNetworking() {
if (!didSetupAccountManagerWithAccessToken()) {
qDebug() << "Cannot setup domain-server automatic networking without an access token.";
qDebug() << "Please add an access token to your config file or via the web interface.";
return;
}
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
const int STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS = 10 * 1000;
const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000;
// setup our timer to check our IP via stun every X seconds
QTimer* dynamicIPTimer = new QTimer(this);
connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN);
_automaticNetworkingSetting =
_settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString();
if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
dynamicIPTimer->start(STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS);
// setup a timer to heartbeat with the ice-server every so often
QTimer* iceHeartbeatTimer = new QTimer(this);
connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates);
iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
// call our sendHeartbeatToIceServer immediately anytime a local or public socket changes
connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer);
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer);
// attempt to update our public socket now, this will send a heartbeat once we get public socket
requestCurrentPublicSocketViaSTUN();
// in case the STUN lookup is still happening we should re-request a public socket once we get that address
connect(&nodeList->getSTUNSockAddr(), &HifiSockAddr::lookupCompleted,
this, &DomainServer::requestCurrentPublicSocketViaSTUN);
}
if (!didSetupAccountManagerWithAccessToken()) {
qDebug() << "Cannot send heartbeat to data server without an access token.";
qDebug() << "Add an access token to your config file or via the web interface.";
return;
}
if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE ||
_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
const QUuid& domainID = nodeList->getSessionUUID();
if (!domainID.isNull()) {
qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID"
<< uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000;
const int STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS = 10 * 1000;
// setup our timer to check our IP via stun every X seconds
QTimer* dynamicIPTimer = new QTimer(this);
connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN);
if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) {
dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS);
// send public socket changes to the data server so nodes can find us at our new IP
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::performIPAddressUpdate);
// attempt to update our sockets now
requestCurrentPublicSocketViaSTUN();
} else {
dynamicIPTimer->start(STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS);
// setup a timer to heartbeat with the ice-server every so often
QTimer* iceHeartbeatTimer = new QTimer(this);
connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates);
iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
// call our sendHeartbeaToIceServer immediately anytime a local or public socket changes
connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer);
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer);
// send our heartbeat to data server so it knows what our network settings are
sendHeartbeatToDataServer();
}
// attempt to update our sockets now
requestCurrentPublicSocketViaSTUN();
} else {
qDebug() << "Cannot enable domain-server automatic networking without a domain ID."
<< "Please add an ID to your config file or via the web interface.";
@ -1164,8 +1177,7 @@ void DomainServer::performICEUpdates() {
}
void DomainServer::sendHeartbeatToIceServer() {
static HifiSockAddr ICE_SERVER_SOCK_ADDR = HifiSockAddr("ice.highfidelity.io", ICE_SERVER_DEFAULT_PORT);
LimitedNodeList::getInstance()->sendHeartbeatToIceServer(ICE_SERVER_SOCK_ADDR);
LimitedNodeList::getInstance()->sendHeartbeatToIceServer(_iceServerSocket);
}
void DomainServer::sendICEPingPackets() {

View file

@ -154,6 +154,8 @@ private:
QString _automaticNetworkingSetting;
DomainServerSettingsManager _settingsManager;
HifiSockAddr _iceServerSocket;
};

View file

@ -245,10 +245,12 @@ function handleGrabBehavior(deltaTime) {
}
// Update for joysticks and move button
var THRUST_DEAD_ZONE = 0.1;
var ROTATE_DEAD_ZONE = 0.1;
function flyWithHydra(deltaTime) {
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
if (thrustJoystickPosition.x != 0 || thrustJoystickPosition.y != 0) {
if (Math.abs(thrustJoystickPosition.x) > THRUST_DEAD_ZONE || Math.abs(thrustJoystickPosition.y) > THRUST_DEAD_ZONE) {
if (thrustMultiplier < MAX_THRUST_MULTIPLIER) {
thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE);
}
@ -270,7 +272,7 @@ function flyWithHydra(deltaTime) {
// View Controller
var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
if (viewJoystickPosition.x != 0 || viewJoystickPosition.y != 0) {
if (Math.abs(viewJoystickPosition.x) > ROTATE_DEAD_ZONE || Math.abs(viewJoystickPosition.y) > ROTATE_DEAD_ZONE) {
// change the body yaw based on our x controller
var orientation = MyAvatar.orientation;

View file

@ -842,7 +842,7 @@ bool Application::event(QEvent* event) {
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
if (!fileEvent->url().isEmpty()) {
AddressManager::getInstance().handleLookupString(fileEvent->url().toLocalFile());
AddressManager::getInstance().handleLookupString(fileEvent->url().toString());
}
return false;
@ -1574,7 +1574,9 @@ void Application::setFullscreen(bool fullscreen) {
}
_window->setWindowState(fullscreen ? (_window->windowState() | Qt::WindowFullScreen) :
(_window->windowState() & ~Qt::WindowFullScreen));
_window->show();
if (!_aboutToQuit) {
_window->show();
}
}
void Application::setEnable3DTVMode(bool enable3DTVMode) {
@ -4013,26 +4015,23 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
return _scriptEnginesHash[scriptURLString];
}
ScriptEngine* scriptEngine;
if (scriptFilename.isNull()) {
scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
} else {
// start the script on a new thread...
scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
if (!scriptEngine->hasScript()) {
qDebug() << "Application::loadScript(), script failed to load...";
QMessageBox::warning(getWindow(), "Error Loading Script", scriptURLString + " failed to load.");
return NULL;
}
_scriptEnginesHash.insertMulti(scriptURLString, scriptEngine);
_runningScriptsWidget->setRunningScripts(getRunningScripts());
UserActivityLogger::getInstance().loadedScript(scriptURLString);
}
ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
scriptEngine->setUserLoaded(isUserLoaded);
registerScriptEngineWithApplicationServices(scriptEngine);
if (scriptFilename.isNull()) {
// this had better be the script editor (we should de-couple so somebody who thinks they are loading a script
// doesn't just get an empty script engine)
// we can complete setup now since there isn't a script we have to load
registerScriptEngineWithApplicationServices(scriptEngine);
} else {
// connect to the appropriate signals of this script engine
connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &Application::handleScriptEngineLoaded);
connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError);
// get the script engine object to load the script at the designated script URL
scriptEngine->loadURL(scriptUrl);
}
// restore the main window's active state
if (activateMainWindow && !loadScriptFromEditor) {
@ -4043,6 +4042,22 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
return scriptEngine;
}
void Application::handleScriptEngineLoaded(const QUrl& scriptURL) {
ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(sender());
_scriptEnginesHash.insertMulti(scriptURL.toString(), scriptEngine);
_runningScriptsWidget->setRunningScripts(getRunningScripts());
UserActivityLogger::getInstance().loadedScript(scriptURL.toString());
// register our application services and set it off on its own thread
registerScriptEngineWithApplicationServices(scriptEngine);
}
void Application::handleScriptLoadError(const QUrl& scriptURL) {
qDebug() << "Application::loadScript(), script failed to load...";
QMessageBox::warning(getWindow(), "Error Loading Script", scriptURL.toString() + " failed to load.");
}
void Application::scriptFinished(const QString& scriptName) {
const QString& scriptURLString = QUrl(scriptName).toString();
QHash<QString, ScriptEngine*>::iterator it = _scriptEnginesHash.find(scriptURLString);

View file

@ -393,6 +393,9 @@ private slots:
void timer();
void idle();
void aboutToQuit();
void handleScriptEngineLoaded(const QUrl& scriptURL);
void handleScriptLoadError(const QUrl& scriptURL);
void connectedToDomain(const QString& hostname);

View file

@ -835,11 +835,16 @@ void GeometryReader::run() {
fbxgeo = readSVO(_reply->readAll());
} else {
bool grabLightmaps = true;
float lightmapLevel = 1.0f;
// HACK: For monday 12/01/2014 we need to kill lighmaps loading in starchamber...
if (_url.path().toLower().endsWith("loungev4_11-18.fbx")) {
grabLightmaps = false;
} else if (_url.path().toLower().endsWith("apt8_reboot.fbx")) {
lightmapLevel = 4.0f;
} else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) {
lightmapLevel = 3.5f;
}
fbxgeo = readFBX(_reply->readAll(), _mapping, grabLightmaps);
fbxgeo = readFBX(_reply->readAll(), _mapping, grabLightmaps, lightmapLevel);
}
QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo));

View file

@ -571,8 +571,6 @@ void ApplicationOverlay::renderControllerPointers() {
static quint64 pressedTime[NUMBER_OF_MAGNIFIERS] = { 0ULL, 0ULL, 0ULL };
static bool isPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false };
static bool stateWhenPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false };
static bool triggerPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false };
static bool bumperPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false };
const HandData* handData = Application::getInstance()->getAvatar()->getHandData();
@ -613,30 +611,6 @@ void ApplicationOverlay::renderControllerPointers() {
}
}
//Check for UI active toggle
if (palmData->getTrigger() == 1.0f) {
if (!triggerPressed[index]) {
if (bumperPressed[index]) {
Menu::getInstance()->setIsOptionChecked(MenuOption::UserInterface,
!Menu::getInstance()->isOptionChecked(MenuOption::UserInterface));
}
triggerPressed[index] = true;
}
} else {
triggerPressed[index] = false;
}
if ((controllerButtons & BUTTON_FWD)) {
if (!bumperPressed[index]) {
if (triggerPressed[index]) {
Menu::getInstance()->setIsOptionChecked(MenuOption::UserInterface,
!Menu::getInstance()->isOptionChecked(MenuOption::UserInterface));
}
bumperPressed[index] = true;
}
} else {
bumperPressed[index] = false;
}
//if we have the oculus, we should make the cursor smaller since it will be
//magnified
if (OculusManager::isConnected()) {

View file

@ -1194,7 +1194,7 @@ int matchTextureUVSetToAttributeChannel(const std::string& texUVSetName, const Q
}
}
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, bool loadLightmaps) {
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) {
QHash<QString, ExtractedMesh> meshes;
QHash<QString, QString> modelIDsToNames;
QHash<QString, int> meshIDsToMeshIndices;
@ -1954,6 +1954,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
FBXTexture emissiveTexture;
glm::vec2 emissiveParams(0.f, 1.f);
emissiveParams.y = lightmapLevel;
QString emissiveTextureID = emissiveTextures.value(childID);
QString ambientTextureID = ambientTextures.value(childID);
if (!emissiveTextureID.isNull() || !ambientTextureID.isNull()) {
@ -2372,10 +2373,10 @@ QByteArray writeMapping(const QVariantHash& mapping) {
return buffer.data();
}
FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps) {
FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) {
QBuffer buffer(const_cast<QByteArray*>(&model));
buffer.open(QIODevice::ReadOnly);
return extractFBXGeometry(parseFBX(&buffer), mapping, loadLightmaps);
return extractFBXGeometry(parseFBX(&buffer), mapping, loadLightmaps, lightmapLevel);
}
bool addMeshVoxelsOperation(OctreeElement* element, void* extraData) {

View file

@ -253,7 +253,7 @@ QByteArray writeMapping(const QVariantHash& mapping);
/// Reads FBX geometry from the supplied model and mapping data.
/// \exception QString if an error occurs in parsing
FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps = true);
FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps = true, float lightmapLevel = 1.0f);
/// Reads SVO geometry from the supplied model data.
FBXGeometry readSVO(const QByteArray& model);

View file

@ -86,6 +86,7 @@ bool HifiSockAddr::operator==(const HifiSockAddr& rhsSockAddr) const {
void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) {
if (hostInfo.error() != QHostInfo::NoError) {
qDebug() << "Lookup failed for" << hostInfo.lookupId() << ":" << hostInfo.errorString();
emit lookupFailed();
}
foreach(const QHostAddress& address, hostInfo.addresses()) {
@ -94,6 +95,7 @@ void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) {
_address = address;
qDebug() << "QHostInfo lookup result for"
<< hostInfo.hostName() << "with lookup ID" << hostInfo.lookupId() << "is" << address.toString();
emit lookupCompleted();
break;
}
}

View file

@ -54,6 +54,9 @@ public:
friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);
private slots:
void handleLookupResult(const QHostInfo& hostInfo);
signals:
void lookupCompleted();
void lookupFailed();
private:
QHostAddress _address;
quint16 _port;

View file

@ -105,6 +105,7 @@ public:
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; }
const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; }
void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
void processKillNode(const QByteArray& datagram);

View file

@ -100,71 +100,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
{
}
ScriptEngine::ScriptEngine(const QUrl& scriptURL,
AbstractControllerScriptingInterface* controllerScriptingInterface) :
_scriptContents(),
_isFinished(false),
_isRunning(false),
_isInitialized(false),
_isAvatar(false),
_avatarIdentityTimer(NULL),
_avatarBillboardTimer(NULL),
_timerFunctionMap(),
_isListeningToAudioStream(false),
_avatarSound(NULL),
_numAvatarSoundSentBytes(0),
_controllerScriptingInterface(controllerScriptingInterface),
_avatarData(NULL),
_scriptName(),
_fileNameString(),
_quatLibrary(),
_vec3Library(),
_uuidLibrary(),
_animationCache(this),
_isUserLoaded(false),
_arrayBufferClass(new ArrayBufferClass(this))
{
QString scriptURLString = scriptURL.toString();
_fileNameString = scriptURLString;
QUrl url(scriptURL);
// if the scheme length is one or lower, maybe they typed in a file, let's try
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
url = QUrl::fromLocalFile(scriptURLString);
}
// ok, let's see if it's valid... and if so, load it
if (url.isValid()) {
if (url.scheme() == "file") {
QString fileName = url.toLocalFile();
QFile scriptFile(fileName);
if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {
qDebug() << "Loading file:" << fileName;
QTextStream in(&scriptFile);
_scriptContents = in.readAll();
} else {
qDebug() << "ERROR Loading file:" << fileName;
emit errorMessage("ERROR Loading file:" + fileName);
}
} else {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
qDebug() << "Downloading script at" << url;
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) {
_scriptContents = reply->readAll();
} else {
qDebug() << "ERROR Loading file:" << url.toString();
emit errorMessage("ERROR Loading file:" + url.toString());
}
}
}
}
void ScriptEngine::setIsAvatar(bool isAvatar) {
_isAvatar = isAvatar;
@ -217,6 +152,56 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents, const QStrin
return true;
}
void ScriptEngine::loadURL(const QUrl& scriptURL) {
if (_isRunning) {
return;
}
QString scriptURLString = scriptURL.toString();
_fileNameString = scriptURLString;
QUrl url(scriptURL);
// if the scheme length is one or lower, maybe they typed in a file, let's try
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
url = QUrl::fromLocalFile(scriptURLString);
}
// ok, let's see if it's valid... and if so, load it
if (url.isValid()) {
if (url.scheme() == "file") {
QString fileName = url.toLocalFile();
QFile scriptFile(fileName);
if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {
qDebug() << "Loading file:" << fileName;
QTextStream in(&scriptFile);
_scriptContents = in.readAll();
emit scriptLoaded(url);
} else {
qDebug() << "ERROR Loading file:" << fileName;
emit errorLoadingScript(url);
}
} else {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, this, &ScriptEngine::handleScriptDownload);
}
}
}
void ScriptEngine::handleScriptDownload() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) {
_scriptContents = reply->readAll();
emit scriptLoaded(reply->url());
} else {
qDebug() << "ERROR Loading file:" << reply->url().toString();
emit errorLoadingScript(reply->url());
}
}
Q_SCRIPT_DECLARE_QMETAOBJECT(LocalVoxels, QString)
void ScriptEngine::init() {

View file

@ -40,9 +40,6 @@ const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 10
class ScriptEngine : public QScriptEngine {
Q_OBJECT
public:
ScriptEngine(const QUrl& scriptURL,
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
ScriptEngine(const QString& scriptContents = NO_SCRIPT,
const QString& fileNameString = QString(""),
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
@ -94,6 +91,7 @@ public:
bool isUserLoaded() const { return _isUserLoaded; }
public slots:
void loadURL(const QUrl& scriptURL);
void stop();
QScriptValue evaluate(const QString& program, const QString& fileName = QString(), int lineNumber = 1);
@ -109,6 +107,8 @@ public slots:
void nodeKilled(SharedNodePointer node);
signals:
void scriptLoaded(const QUrl& scriptURL);
void errorLoadingScript(const QUrl& scriptURL);
void update(float deltaTime);
void scriptEnding();
void finished(const QString& fileNameString);
@ -155,6 +155,8 @@ private:
ArrayBufferClass* _arrayBufferClass;
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
private slots:
void handleScriptDownload();
};
#endif // hifi_ScriptEngine_h