mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-08 06:32:35 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into fix-linux-asan
This commit is contained in:
commit
5e2aeb96d3
31 changed files with 444 additions and 202 deletions
|
@ -103,11 +103,6 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
);
|
||||
|
||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
|
||||
connect(nodeList.data(), &NodeList::nodeAdded, this, [this](const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::DownstreamAudioMixer) {
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AudioMixer::queueAudioPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) {
|
||||
|
@ -389,7 +384,10 @@ void AudioMixer::start() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// prepare the NodeList
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::DownstreamAudioMixer, NodeType::EntityScriptServer });
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
||||
NodeType::Agent, NodeType::EntityScriptServer,
|
||||
NodeType::UpstreamAudioMixer, NodeType::DownstreamAudioMixer
|
||||
});
|
||||
nodeList->linkedDataCreateCallback = [&](Node* node) { getOrCreateClientData(node); };
|
||||
|
||||
// parse out any AudioMixer settings
|
||||
|
|
|
@ -51,6 +51,11 @@ public:
|
|||
static const QVector<ReverbSettings>& getReverbSettings() { return _zoneReverbSettings; }
|
||||
static const std::pair<QString, CodecPluginPointer> negotiateCodec(std::vector<QString> codecs);
|
||||
|
||||
static bool shouldReplicateTo(const Node& from, const Node& to) {
|
||||
return to.getType() == NodeType::DownstreamAudioMixer &&
|
||||
to.getPublicSocket() != from.getPublicSocket() &&
|
||||
to.getLocalSocket() != from.getLocalSocket();
|
||||
}
|
||||
public slots:
|
||||
void run() override;
|
||||
void sendStatsPacket() override;
|
||||
|
|
|
@ -67,24 +67,25 @@ void AudioMixerClientData::processPackets() {
|
|||
case PacketType::MicrophoneAudioNoEcho:
|
||||
case PacketType::MicrophoneAudioWithEcho:
|
||||
case PacketType::InjectAudio:
|
||||
case PacketType::AudioStreamStats:
|
||||
case PacketType::SilentAudioFrame:
|
||||
case PacketType::ReplicatedMicrophoneAudioNoEcho:
|
||||
case PacketType::ReplicatedMicrophoneAudioWithEcho:
|
||||
case PacketType::ReplicatedInjectAudio:
|
||||
case PacketType::ReplicatedSilentAudioFrame: {
|
||||
case PacketType::SilentAudioFrame: {
|
||||
|
||||
if (node->isUpstream() && !_hasSetupCodecForUpstreamNode) {
|
||||
if (node->isUpstream()) {
|
||||
setupCodecForReplicatedAgent(packet);
|
||||
}
|
||||
|
||||
QMutexLocker lock(&getMutex());
|
||||
parseData(*packet);
|
||||
|
||||
|
||||
optionallyReplicatePacket(*packet, *node);
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketType::AudioStreamStats: {
|
||||
QMutexLocker lock(&getMutex());
|
||||
parseData(*packet);
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketType::NegotiateAudioFormat:
|
||||
negotiateAudioFormat(*packet, node);
|
||||
break;
|
||||
|
@ -141,7 +142,7 @@ void AudioMixerClientData::optionallyReplicatePacket(ReceivedMessage& message, c
|
|||
|
||||
// enumerate the downstream audio mixers and send them the replicated version of this packet
|
||||
nodeList->unsafeEachNode([&](const SharedNodePointer& downstreamNode) {
|
||||
if (downstreamNode->getType() == NodeType::DownstreamAudioMixer) {
|
||||
if (AudioMixer::shouldReplicateTo(node, *downstreamNode)) {
|
||||
// construct the packet only once, if we have any downstream audio mixers to send to
|
||||
if (!packet) {
|
||||
// construct an NLPacket to send to the replicant that has the contents of the received packet
|
||||
|
@ -687,14 +688,14 @@ void AudioMixerClientData::setupCodecForReplicatedAgent(QSharedPointer<ReceivedM
|
|||
// pull the codec string from the packet
|
||||
auto codecString = message->readString();
|
||||
|
||||
qDebug() << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID())
|
||||
if (codecString != _selectedCodecName) {
|
||||
qDebug() << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID())
|
||||
<< "-" << codecString;
|
||||
|
||||
const std::pair<QString, CodecPluginPointer> codec = AudioMixer::negotiateCodec({ codecString });
|
||||
setupCodec(codec.second, codec.first);
|
||||
const std::pair<QString, CodecPluginPointer> codec = AudioMixer::negotiateCodec({ codecString });
|
||||
setupCodec(codec.second, codec.first);
|
||||
|
||||
_hasSetupCodecForUpstreamNode = true;
|
||||
|
||||
// seek back to the beginning of the message so other readers are in the right place
|
||||
message->seek(0);
|
||||
// seek back to the beginning of the message so other readers are in the right place
|
||||
message->seek(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,8 +184,6 @@ private:
|
|||
|
||||
bool _shouldMuteClient { false };
|
||||
bool _requestsDomainListData { false };
|
||||
|
||||
bool _hasSetupCodecForUpstreamNode { false };
|
||||
};
|
||||
|
||||
#endif // hifi_AudioMixerClientData_h
|
||||
|
|
|
@ -66,7 +66,6 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
|||
connect(nodeList.data(), &NodeList::nodeAdded, this, [this](const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::DownstreamAvatarMixer) {
|
||||
getOrCreateClientData(node);
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -144,8 +143,8 @@ void AvatarMixer::optionallyReplicatePacket(ReceivedMessage& message, const Node
|
|||
std::unique_ptr<NLPacket> packet;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachMatchingNode([&](const SharedNodePointer& node) {
|
||||
return node->getType() == NodeType::DownstreamAvatarMixer;
|
||||
nodeList->eachMatchingNode([&](const SharedNodePointer& downstreamNode) {
|
||||
return shouldReplicateTo(node, *downstreamNode);
|
||||
}, [&](const SharedNodePointer& node) {
|
||||
if (!packet) {
|
||||
// construct an NLPacket to send to the replicant that has the contents of the received packet
|
||||
|
@ -165,10 +164,6 @@ void AvatarMixer::queueIncomingPacket(QSharedPointer<ReceivedMessage> message, S
|
|||
_queueIncomingPacketElapsedTime += (end - start);
|
||||
}
|
||||
|
||||
|
||||
AvatarMixer::~AvatarMixer() {
|
||||
}
|
||||
|
||||
void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
|
||||
if (destinationNode->getType() == NodeType::Agent && !destinationNode->isUpstream()) {
|
||||
QByteArray individualData = nodeData->getAvatar().identityByteArray();
|
||||
|
@ -429,8 +424,8 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
|
|||
nodeList->eachMatchingNode([&](const SharedNodePointer& node) {
|
||||
// we relay avatar kill packets to agents that are not upstream
|
||||
// and downstream avatar mixers, if the node that was just killed was being replicated
|
||||
return (node->getType() == NodeType::Agent && !node->isUpstream())
|
||||
|| (killedNode->isReplicated() && node->getType() == NodeType::DownstreamAvatarMixer);
|
||||
return (node->getType() == NodeType::Agent && !node->isUpstream()) ||
|
||||
(killedNode->isReplicated() && shouldReplicateTo(*killedNode, *node));
|
||||
}, [&](const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::Agent) {
|
||||
if (!killPacket) {
|
||||
|
@ -862,7 +857,10 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node
|
|||
|
||||
void AvatarMixer::domainSettingsRequestComplete() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::DownstreamAvatarMixer, NodeType::EntityScriptServer });
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
||||
NodeType::Agent, NodeType::EntityScriptServer,
|
||||
NodeType::UpstreamAvatarMixer, NodeType::DownstreamAvatarMixer
|
||||
});
|
||||
|
||||
// parse the settings to pull out the values we need
|
||||
parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject());
|
||||
|
|
|
@ -28,7 +28,13 @@ class AvatarMixer : public ThreadedAssignment {
|
|||
Q_OBJECT
|
||||
public:
|
||||
AvatarMixer(ReceivedMessage& message);
|
||||
~AvatarMixer();
|
||||
|
||||
static bool shouldReplicateTo(const Node& from, const Node& to) {
|
||||
return to.getType() == NodeType::DownstreamAvatarMixer &&
|
||||
to.getPublicSocket() != from.getPublicSocket() &&
|
||||
to.getLocalSocket() != from.getLocalSocket();
|
||||
}
|
||||
|
||||
public slots:
|
||||
/// runs the avatar mixer
|
||||
void run() override;
|
||||
|
|
|
@ -79,13 +79,13 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData,
|
|||
}
|
||||
}
|
||||
|
||||
int AvatarMixerSlave::sendReplicatedIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
|
||||
if (destinationNode->getType() == NodeType::DownstreamAvatarMixer) {
|
||||
int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) {
|
||||
if (AvatarMixer::shouldReplicateTo(agentNode, destinationNode)) {
|
||||
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray();
|
||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
|
||||
auto identityPacket = NLPacket::create(PacketType::ReplicatedAvatarIdentity);
|
||||
auto identityPacket = NLPacketList::create(PacketType::ReplicatedAvatarIdentity, QByteArray(), true, true);
|
||||
identityPacket->write(individualData);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(*identityPacket, *destinationNode);
|
||||
DependencyManager::get<NodeList>()->sendPacketList(std::move(identityPacket), destinationNode);
|
||||
_stats.numIdentityPackets++;
|
||||
return individualData.size();
|
||||
} else {
|
||||
|
@ -453,6 +453,10 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin
|
|||
nodeData->resetNumAvatarsSentLastFrame();
|
||||
|
||||
std::for_each(_begin, _end, [&](const SharedNodePointer& agentNode) {
|
||||
if (!AvatarMixer::shouldReplicateTo(*agentNode, *node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// collect agents that we have avatar data for that we are supposed to replicate
|
||||
if (agentNode->getType() == NodeType::Agent && agentNode->getLinkedData() && agentNode->isReplicated()) {
|
||||
const AvatarMixerClientData* agentNodeData = reinterpret_cast<const AvatarMixerClientData*>(agentNode->getLinkedData());
|
||||
|
@ -479,7 +483,7 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin
|
|||
auto lastBroadcastTime = nodeData->getLastBroadcastTime(agentNode->getUUID());
|
||||
if (lastBroadcastTime <= agentNodeData->getIdentityChangeTimestamp()
|
||||
|| (start - lastBroadcastTime) >= REBROADCAST_IDENTITY_TO_DOWNSTREAM_EVERY_US) {
|
||||
sendReplicatedIdentityPacket(agentNodeData, node);
|
||||
sendReplicatedIdentityPacket(*agentNode, agentNodeData, *node);
|
||||
nodeData->setLastBroadcastTime(agentNode->getUUID(), start);
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ public:
|
|||
|
||||
private:
|
||||
int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode);
|
||||
int sendReplicatedIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode);
|
||||
int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode);
|
||||
|
||||
void broadcastAvatarDataToAgent(const SharedNodePointer& node);
|
||||
void broadcastAvatarDataToDownstreamMixer(const SharedNodePointer& node);
|
||||
|
|
|
@ -504,6 +504,10 @@ Function PostInstallOptionsPage
|
|||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetPostInstallOption $LaunchServerNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
${StrContains} $substringResult "/forceNoLaunchServer" $CMDLINE
|
||||
${IfNot} $substringResult == ""
|
||||
${NSD_SetState} $LaunchServerNowCheckbox ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
${EndIf}
|
||||
|
@ -514,6 +518,10 @@ Function PostInstallOptionsPage
|
|||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetPostInstallOption $LaunchClientNowCheckbox @CLIENT_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
${StrContains} $substringResult "/forceNoLaunchClient" $CMDLINE
|
||||
${IfNot} $substringResult == ""
|
||||
${NSD_SetState} $LaunchClientNowCheckbox ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
${If} @PR_BUILD@ == 1
|
||||
|
|
|
@ -1339,6 +1339,7 @@
|
|||
{
|
||||
"name": "broadcasting",
|
||||
"label": "Broadcasting",
|
||||
"restart": false,
|
||||
"settings": [
|
||||
{
|
||||
"name": "users",
|
||||
|
@ -1395,6 +1396,46 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "upstream_servers",
|
||||
"label": "Broadcasting Servers",
|
||||
"assignment-types": [0,1],
|
||||
"type": "table",
|
||||
"advanced": true,
|
||||
"can_add_new_rows": true,
|
||||
"help": "Servers that broadcast data to this domain",
|
||||
"numbered": false,
|
||||
"columns": [
|
||||
{
|
||||
"name": "address",
|
||||
"label": "Address",
|
||||
"can_set": true
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"label": "Port",
|
||||
"can_set": true
|
||||
},
|
||||
{
|
||||
"name": "server_type",
|
||||
"label": "Server Type",
|
||||
"type": "select",
|
||||
"placeholder": "Audio Mixer",
|
||||
"default": "Audio Mixer",
|
||||
"can_set": true,
|
||||
"options": [
|
||||
{
|
||||
"value": "Audio Mixer",
|
||||
"label": "Audio Mixer"
|
||||
},
|
||||
{
|
||||
"value": "Avatar Mixer",
|
||||
"label": "Avatar Mixer"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<link href="/css/sweetalert.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/bootstrap-switch.min.css" rel="stylesheet" media="screen">
|
||||
|
||||
<script src='/js/sweetalert.min.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
|
@ -37,6 +38,8 @@
|
|||
</li>
|
||||
<li><a href="/content/">Content</a></li>
|
||||
<li><a href="/settings/">Settings</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-right navbar-nav">
|
||||
<li><a href="#" id="restart-server"><span class="glyphicon glyphicon-refresh"></span> Restart</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -33,9 +33,17 @@ $(document).ready(function(){
|
|||
return this.href == url;
|
||||
}).parent().addClass('active');
|
||||
|
||||
$('body').on('click', '#restart-server', function(e){
|
||||
$.get("/restart");
|
||||
showRestartModal();
|
||||
$('body').on('click', '#restart-server', function(e) {
|
||||
swal( {
|
||||
title: "Are you sure?",
|
||||
text: "This will restart your domain server, causing your domain to be briefly offline.",
|
||||
type: "warning",
|
||||
html: true,
|
||||
showCancelButton: true
|
||||
}, function() {
|
||||
$.get("/restart");
|
||||
showRestartModal();
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
|
@ -86,7 +86,6 @@
|
|||
<script src='/js/underscore-keypath.min.js'></script>
|
||||
<script src='/js/bootbox.min.js'></script>
|
||||
<script src='js/bootstrap-switch.min.js'></script>
|
||||
<script src='/js/sweetalert.min.js'></script>
|
||||
<script src='js/settings.js'></script>
|
||||
<script src='js/form2js.min.js'></script>
|
||||
<script src='js/sha256.js'></script>
|
||||
|
|
|
@ -424,7 +424,11 @@ function postSettings(jsonSettings) {
|
|||
type: 'POST'
|
||||
}).done(function(data){
|
||||
if (data.status == "success") {
|
||||
showRestartModal();
|
||||
if ($(".save-button").html() === SAVE_BUTTON_LABEL_RESTART) {
|
||||
showRestartModal();
|
||||
} else {
|
||||
location.reload(true);
|
||||
}
|
||||
} else {
|
||||
showErrorMessage("Error", SETTINGS_ERROR_MESSAGE)
|
||||
reloadSettings();
|
||||
|
|
|
@ -87,7 +87,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
qDebug() << "[VERSION] VERSION:" << BuildInfo::VERSION;
|
||||
qDebug() << "[VERSION] BUILD_BRANCH:" << BuildInfo::BUILD_BRANCH;
|
||||
qDebug() << "[VERSION] BUILD_GLOBAL_SERVICES:" << BuildInfo::BUILD_GLOBAL_SERVICES;
|
||||
qDebug() << "[VERSION] We will be using this default ICE server:" << ICE_SERVER_DEFAULT_HOSTNAME;
|
||||
qDebug() << "[VERSION] We will be using this name to find ICE servers:" << _iceServerAddr;
|
||||
|
||||
|
||||
// make sure we have a fresh AccountManager instance
|
||||
|
@ -121,6 +121,8 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
this, &DomainServer::updateReplicatedNodes);
|
||||
connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated,
|
||||
this, &DomainServer::updateDownstreamNodes);
|
||||
connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated,
|
||||
this, &DomainServer::updateUpstreamNodes);
|
||||
|
||||
setupGroupCacheRefresh();
|
||||
|
||||
|
@ -135,6 +137,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
updateReplicatedNodes();
|
||||
updateDownstreamNodes();
|
||||
updateUpstreamNodes();
|
||||
|
||||
if (_type != NonMetaverse) {
|
||||
// if we have a metaverse domain, we'll use an access token for API calls
|
||||
|
@ -1549,7 +1552,7 @@ void DomainServer::sendHeartbeatToIceServer() {
|
|||
|
||||
} else {
|
||||
qDebug() << "Not sending ice-server heartbeat since there is no selected ice-server.";
|
||||
qDebug() << "Waiting for" << ICE_SERVER_DEFAULT_HOSTNAME << "host lookup response";
|
||||
qDebug() << "Waiting for" << _iceServerAddr << "host lookup response";
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2229,53 +2232,84 @@ void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer&
|
|||
|
||||
static const QString BROADCASTING_SETTINGS_KEY = "broadcasting";
|
||||
|
||||
void DomainServer::updateDownstreamNodes() {
|
||||
struct ReplicationServerInfo {
|
||||
NodeType_t nodeType;
|
||||
HifiSockAddr sockAddr;
|
||||
};
|
||||
|
||||
ReplicationServerInfo serverInformationFromSettings(QVariantMap serverMap, ReplicationServerDirection direction) {
|
||||
static const QString REPLICATION_SERVER_ADDRESS = "address";
|
||||
static const QString REPLICATION_SERVER_PORT = "port";
|
||||
static const QString REPLICATION_SERVER_TYPE = "server_type";
|
||||
|
||||
if (serverMap.contains(REPLICATION_SERVER_ADDRESS) && serverMap.contains(REPLICATION_SERVER_PORT)
|
||||
&& serverMap.contains(REPLICATION_SERVER_TYPE)) {
|
||||
|
||||
auto nodeType = NodeType::fromString(serverMap[REPLICATION_SERVER_TYPE].toString());
|
||||
|
||||
ReplicationServerInfo serverInfo;
|
||||
|
||||
if (direction == Upstream) {
|
||||
serverInfo.nodeType = NodeType::upstreamType(nodeType);
|
||||
} else if (direction == Downstream) {
|
||||
serverInfo.nodeType = NodeType::downstreamType(nodeType);
|
||||
}
|
||||
|
||||
// read the address and port and construct a HifiSockAddr from them
|
||||
serverInfo.sockAddr = {
|
||||
serverMap[REPLICATION_SERVER_ADDRESS].toString(),
|
||||
(quint16) serverMap[REPLICATION_SERVER_PORT].toString().toInt()
|
||||
};
|
||||
|
||||
return serverInfo;
|
||||
}
|
||||
|
||||
return { NodeType::Unassigned, HifiSockAddr() };
|
||||
}
|
||||
|
||||
void DomainServer::updateReplicationNodes(ReplicationServerDirection direction) {
|
||||
auto settings = _settingsManager.getSettingsMap();
|
||||
|
||||
if (settings.contains(BROADCASTING_SETTINGS_KEY)) {
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
std::vector<HifiSockAddr> downstreamNodesInSettings;
|
||||
auto replicationSettings = settings.value(BROADCASTING_SETTINGS_KEY).toMap();
|
||||
if (replicationSettings.contains("downstream_servers")) {
|
||||
auto serversSettings = replicationSettings.value("downstream_servers").toList();
|
||||
std::vector<HifiSockAddr> replicationNodesInSettings;
|
||||
|
||||
std::vector<HifiSockAddr> knownDownstreamNodes;
|
||||
auto replicationSettings = settings.value(BROADCASTING_SETTINGS_KEY).toMap();
|
||||
|
||||
QString serversKey = direction == Upstream ? "upstream_servers" : "downstream_servers";
|
||||
QString replicationDirection = direction == Upstream ? "upstream" : "downstream";
|
||||
|
||||
if (replicationSettings.contains(serversKey)) {
|
||||
auto serversSettings = replicationSettings.value(serversKey).toList();
|
||||
|
||||
std::vector<HifiSockAddr> knownReplicationNodes;
|
||||
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
if (NodeType::isDownstream(otherNode->getType())) {
|
||||
knownDownstreamNodes.push_back(otherNode->getPublicSocket());
|
||||
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|
||||
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
|
||||
knownReplicationNodes.push_back(otherNode->getPublicSocket());
|
||||
}
|
||||
});
|
||||
|
||||
for (auto& server : serversSettings) {
|
||||
auto downstreamServer = server.toMap();
|
||||
auto replicationServer = serverInformationFromSettings(server.toMap(), direction);
|
||||
|
||||
static const QString DOWNSTREAM_SERVER_ADDRESS = "address";
|
||||
static const QString DOWNSTREAM_SERVER_PORT = "port";
|
||||
static const QString DOWNSTREAM_SERVER_TYPE = "server_type";
|
||||
if (!replicationServer.sockAddr.isNull() && replicationServer.nodeType != NodeType::Unassigned) {
|
||||
// make sure we have the settings we need for this replication server
|
||||
replicationNodesInSettings.push_back(replicationServer.sockAddr);
|
||||
|
||||
// make sure we have the settings we need for this downstream server
|
||||
if (downstreamServer.contains(DOWNSTREAM_SERVER_ADDRESS) && downstreamServer.contains(DOWNSTREAM_SERVER_PORT)) {
|
||||
|
||||
auto nodeType = NodeType::fromString(downstreamServer[DOWNSTREAM_SERVER_TYPE].toString());
|
||||
auto downstreamNodeType = NodeType::downstreamType(nodeType);
|
||||
|
||||
// read the address and port and construct a HifiSockAddr from them
|
||||
HifiSockAddr downstreamServerAddr {
|
||||
downstreamServer[DOWNSTREAM_SERVER_ADDRESS].toString(),
|
||||
(quint16) downstreamServer[DOWNSTREAM_SERVER_PORT].toString().toInt()
|
||||
};
|
||||
downstreamNodesInSettings.push_back(downstreamServerAddr);
|
||||
|
||||
bool knownNode = find(knownDownstreamNodes.cbegin(), knownDownstreamNodes.cend(),
|
||||
downstreamServerAddr) != knownDownstreamNodes.cend();
|
||||
bool knownNode = find(knownReplicationNodes.cbegin(), knownReplicationNodes.cend(),
|
||||
replicationServer.sockAddr) != knownReplicationNodes.cend();
|
||||
if (!knownNode) {
|
||||
// manually add the downstream node to our node list
|
||||
auto node = nodeList->addOrUpdateNode(QUuid::createUuid(), downstreamNodeType,
|
||||
downstreamServerAddr, downstreamServerAddr);
|
||||
// manually add the replication node to our node list
|
||||
auto node = nodeList->addOrUpdateNode(QUuid::createUuid(), replicationServer.nodeType,
|
||||
replicationServer.sockAddr, replicationServer.sockAddr,
|
||||
false, direction == Upstream);
|
||||
node->setIsForcedNeverSilent(true);
|
||||
|
||||
qDebug() << "Adding downstream node:" << node->getUUID() << downstreamServerAddr;
|
||||
qDebug() << "Adding" << (direction == Upstream ? "upstream" : "downstream")
|
||||
<< "node:" << node->getUUID() << replicationServer.sockAddr;
|
||||
|
||||
// manually activate the public socket for the downstream node
|
||||
// manually activate the public socket for the replication node
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
}
|
||||
|
@ -2288,11 +2322,13 @@ void DomainServer::updateDownstreamNodes() {
|
|||
// we cannot recursively take the write lock required by handleKillNode)
|
||||
std::vector<SharedNodePointer> nodesToKill;
|
||||
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
if (NodeType::isDownstream(otherNode->getType())) {
|
||||
bool nodeInSettings = find(downstreamNodesInSettings.cbegin(), downstreamNodesInSettings.cend(),
|
||||
otherNode->getPublicSocket()) != downstreamNodesInSettings.cend();
|
||||
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|
||||
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
|
||||
bool nodeInSettings = find(replicationNodesInSettings.cbegin(), replicationNodesInSettings.cend(),
|
||||
otherNode->getPublicSocket()) != replicationNodesInSettings.cend();
|
||||
if (!nodeInSettings) {
|
||||
qDebug() << "Removing downstream node:" << otherNode->getUUID() << otherNode->getPublicSocket();
|
||||
qDebug() << "Removing" << replicationDirection
|
||||
<< "node:" << otherNode->getUUID() << otherNode->getPublicSocket();
|
||||
nodesToKill.push_back(otherNode);
|
||||
}
|
||||
}
|
||||
|
@ -2304,6 +2340,14 @@ void DomainServer::updateDownstreamNodes() {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::updateDownstreamNodes() {
|
||||
updateReplicationNodes(Downstream);
|
||||
}
|
||||
|
||||
void DomainServer::updateUpstreamNodes() {
|
||||
updateReplicationNodes(Upstream);
|
||||
}
|
||||
|
||||
void DomainServer::updateReplicatedNodes() {
|
||||
// Make sure we have downstream nodes in our list
|
||||
auto settings = _settingsManager.getSettingsMap();
|
||||
|
@ -2639,7 +2683,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) {
|
|||
_iceAddressLookupID = -1;
|
||||
|
||||
if (hostInfo.error() != QHostInfo::NoError) {
|
||||
qWarning() << "IP address lookup failed for" << ICE_SERVER_DEFAULT_HOSTNAME << ":" << hostInfo.errorString();
|
||||
qWarning() << "IP address lookup failed for" << _iceServerAddr << ":" << hostInfo.errorString();
|
||||
|
||||
// if we don't have an ICE server to use yet, trigger a retry
|
||||
if (_iceServerSocket.isNull()) {
|
||||
|
@ -2654,7 +2698,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) {
|
|||
_iceServerAddresses = hostInfo.addresses();
|
||||
|
||||
if (countBefore == 0) {
|
||||
qInfo() << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << ICE_SERVER_DEFAULT_HOSTNAME;
|
||||
qInfo() << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << _iceServerAddr;
|
||||
}
|
||||
|
||||
if (_iceServerSocket.isNull()) {
|
||||
|
@ -2689,7 +2733,7 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) {
|
|||
// so clear the set of failed addresses and start going through them again
|
||||
|
||||
qWarning() << "All current ice-server addresses have failed - re-attempting all current addresses for"
|
||||
<< ICE_SERVER_DEFAULT_HOSTNAME;
|
||||
<< _iceServerAddr;
|
||||
|
||||
_failedIceServerAddresses.clear();
|
||||
candidateICEAddresses = _iceServerAddresses;
|
||||
|
|
|
@ -39,6 +39,11 @@ typedef QMultiHash<QUuid, WalletTransaction*> TransactionHash;
|
|||
using Subnet = QPair<QHostAddress, int>;
|
||||
using SubnetList = std::vector<Subnet>;
|
||||
|
||||
enum ReplicationServerDirection {
|
||||
Upstream,
|
||||
Downstream
|
||||
};
|
||||
|
||||
class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -104,6 +109,7 @@ private slots:
|
|||
|
||||
void updateReplicatedNodes();
|
||||
void updateDownstreamNodes();
|
||||
void updateUpstreamNodes();
|
||||
|
||||
signals:
|
||||
void iceServerChanged();
|
||||
|
@ -170,6 +176,8 @@ private:
|
|||
|
||||
QString pathForRedirect(QString path = QString()) const;
|
||||
|
||||
void updateReplicationNodes(ReplicationServerDirection direction);
|
||||
|
||||
SubnetList _acSubnetWhitelist;
|
||||
|
||||
std::vector<QString> _replicatedUsernames;
|
||||
|
|
|
@ -903,6 +903,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_saveAvatarOverrideUrl = true;
|
||||
}
|
||||
|
||||
QString defaultScriptsLocation = getCmdOption(argc, constArgv, "--scripts");
|
||||
if (!defaultScriptsLocation.isEmpty()) {
|
||||
PathUtils::defaultScriptsLocation(defaultScriptsLocation);
|
||||
}
|
||||
|
||||
_glWidget = new GLCanvas();
|
||||
getApplicationCompositor().setRenderingWidget(_glWidget);
|
||||
_window->setCentralWidget(_glWidget);
|
||||
|
@ -1167,7 +1172,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// force the model the look at the correct directory (weird order of operations issue)
|
||||
scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation());
|
||||
// do this as late as possible so that all required subsystems are initialized
|
||||
scriptEngines->loadScripts();
|
||||
// If we've overridden the default scripts location, just load default scripts
|
||||
// otherwise, load 'em all
|
||||
if (!defaultScriptsLocation.isEmpty()) {
|
||||
scriptEngines->loadDefaultScripts();
|
||||
scriptEngines->defaultScriptsLocationOverridden(true);
|
||||
} else {
|
||||
scriptEngines->loadScripts();
|
||||
}
|
||||
|
||||
// Make sure we don't time out during slow operations at startup
|
||||
updateHeartbeat();
|
||||
|
||||
|
@ -5855,7 +5868,7 @@ void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const
|
|||
|
||||
void Application::showScriptLogs() {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/debugging/debugWindow.js");
|
||||
scriptEngines->loadScript(defaultScriptsLoc.toString());
|
||||
}
|
||||
|
|
|
@ -303,7 +303,7 @@ Menu::Menu() {
|
|||
// Settings > Avatar...
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "Avatar...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AvatarPreferencesDialog.qml"),
|
||||
qApp->showDialog(QString("hifi/dialogs/AvatarPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletAvatarPreferences.qml"), "AvatarPreferencesDialog");
|
||||
});
|
||||
|
||||
|
@ -629,7 +629,7 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(audioDebugMenu, "Stats...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/audio/stats.js");
|
||||
scriptEngines->loadScript(defaultScriptsLoc.toString());
|
||||
});
|
||||
|
|
|
@ -9,18 +9,30 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "AudioDevices.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "AudioClient.h"
|
||||
#include "Audio.h"
|
||||
|
||||
#include "UserActivityLogger.h"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
Setting::Handle<QString> inputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }};
|
||||
Setting::Handle<QString> outputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }};
|
||||
Setting::Handle<QString> inputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }};
|
||||
Setting::Handle<QString> outputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }};
|
||||
static Setting::Handle<QString> desktopInputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }};
|
||||
static Setting::Handle<QString> desktopOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }};
|
||||
static Setting::Handle<QString> hmdInputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }};
|
||||
static Setting::Handle<QString> hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }};
|
||||
|
||||
Setting::Handle<QString>& getSetting(bool contextIsHMD, QAudio::Mode mode) {
|
||||
if (mode == QAudio::AudioInput) {
|
||||
return contextIsHMD ? hmdInputDeviceSetting : desktopInputDeviceSetting;
|
||||
} else { // if (mode == QAudio::AudioOutput)
|
||||
return contextIsHMD ? hmdOutputDeviceSetting : desktopOutputDeviceSetting;
|
||||
}
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> AudioDeviceList::_roles {
|
||||
{ Qt::DisplayRole, "display" },
|
||||
|
@ -43,32 +55,37 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
|
|||
}
|
||||
|
||||
bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, int role) {
|
||||
if (!index.isValid() || index.row() >= _devices.size()) {
|
||||
if (!index.isValid() || index.row() >= _devices.size() || role != Qt::CheckStateRole) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// only allow switching to a new device, not deactivating an in-use device
|
||||
auto selected = value.toBool();
|
||||
if (!selected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return setDevice(index.row(), true);
|
||||
}
|
||||
|
||||
bool AudioDeviceList::setDevice(int row, bool fromUser) {
|
||||
bool success = false;
|
||||
auto& device = _devices[row];
|
||||
|
||||
if (role == Qt::CheckStateRole) {
|
||||
auto selected = value.toBool();
|
||||
auto& device = _devices[index.row()];
|
||||
// skip if already selected
|
||||
if (!device.selected) {
|
||||
auto client = DependencyManager::get<AudioClient>();
|
||||
QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, success),
|
||||
Q_ARG(QAudio::Mode, _mode),
|
||||
Q_ARG(const QAudioDeviceInfo&, device.info));
|
||||
|
||||
// only allow switching to a new device, not deactivating an in-use device
|
||||
if (selected
|
||||
// skip if already selected
|
||||
&& selected != device.selected) {
|
||||
|
||||
auto client = DependencyManager::get<AudioClient>();
|
||||
QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, success),
|
||||
Q_ARG(QAudio::Mode, _mode),
|
||||
Q_ARG(const QAudioDeviceInfo&, device.info));
|
||||
|
||||
if (success) {
|
||||
device.selected = true;
|
||||
emit deviceSelected(device.info);
|
||||
emit deviceChanged(device.info);
|
||||
if (success) {
|
||||
device.selected = true;
|
||||
if (fromUser) {
|
||||
emit deviceSelected(device.info, _selectedDevice);
|
||||
}
|
||||
emit deviceChanged(device.info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,12 +105,12 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) {
|
|||
}
|
||||
}
|
||||
if (i < rowCount()) {
|
||||
success = setData(createIndex(i, 0), true, Qt::CheckStateRole);
|
||||
success = setDevice(i, false);
|
||||
}
|
||||
|
||||
// the selection failed - reset it
|
||||
if (!success) {
|
||||
emit deviceSelected(QAudioDeviceInfo());
|
||||
emit deviceSelected();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,48 +184,55 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) {
|
|||
_inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput));
|
||||
_outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput));
|
||||
|
||||
connect(&_inputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onInputDeviceSelected);
|
||||
connect(&_outputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onOutputDeviceSelected);
|
||||
connect(&_inputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) {
|
||||
onDeviceSelected(QAudio::AudioInput, device, previousDevice);
|
||||
});
|
||||
connect(&_outputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) {
|
||||
onDeviceSelected(QAudio::AudioOutput, device, previousDevice);
|
||||
});
|
||||
}
|
||||
|
||||
void AudioDevices::onContextChanged(const QString& context) {
|
||||
QString input;
|
||||
QString output;
|
||||
if (_contextIsHMD) {
|
||||
input = inputDeviceHMD.get();
|
||||
output = outputDeviceHMD.get();
|
||||
} else {
|
||||
input = inputDeviceDesktop.get();
|
||||
output = outputDeviceDesktop.get();
|
||||
}
|
||||
auto input = getSetting(_contextIsHMD, QAudio::AudioInput).get();
|
||||
auto output = getSetting(_contextIsHMD, QAudio::AudioOutput).get();
|
||||
|
||||
_inputs.resetDevice(_contextIsHMD, input);
|
||||
_outputs.resetDevice(_contextIsHMD, output);
|
||||
}
|
||||
|
||||
void AudioDevices::onInputDeviceSelected(const QAudioDeviceInfo& device) {
|
||||
QString deviceName;
|
||||
void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) {
|
||||
QString deviceName = device.isNull() ? QString() : device.deviceName();
|
||||
|
||||
auto& setting = getSetting(_contextIsHMD, mode);
|
||||
|
||||
// check for a previous device
|
||||
auto wasDefault = setting.get().isNull();
|
||||
|
||||
// store the selected device
|
||||
setting.set(deviceName);
|
||||
|
||||
// log the selected device
|
||||
if (!device.isNull()) {
|
||||
deviceName = device.deviceName();
|
||||
}
|
||||
QJsonObject data;
|
||||
|
||||
if (_contextIsHMD) {
|
||||
inputDeviceHMD.set(deviceName);
|
||||
} else {
|
||||
inputDeviceDesktop.set(deviceName);
|
||||
}
|
||||
}
|
||||
const QString MODE = "audio_mode";
|
||||
const QString INPUT = "INPUT";
|
||||
const QString OUTPUT = "OUTPUT"; data[MODE] = mode == QAudio::AudioInput ? INPUT : OUTPUT;
|
||||
|
||||
void AudioDevices::onOutputDeviceSelected(const QAudioDeviceInfo& device) {
|
||||
QString deviceName;
|
||||
if (!device.isNull()) {
|
||||
deviceName = device.deviceName();
|
||||
}
|
||||
const QString CONTEXT = "display_mode";
|
||||
data[CONTEXT] = _contextIsHMD ? Audio::HMD : Audio::DESKTOP;
|
||||
|
||||
if (_contextIsHMD) {
|
||||
outputDeviceHMD.set(deviceName);
|
||||
} else {
|
||||
outputDeviceDesktop.set(deviceName);
|
||||
const QString DISPLAY = "display_device";
|
||||
data[DISPLAY] = qApp->getActiveDisplayPlugin()->getName();
|
||||
|
||||
const QString DEVICE = "device";
|
||||
const QString PREVIOUS_DEVICE = "previous_device";
|
||||
const QString WAS_DEFAULT = "was_default";
|
||||
data[DEVICE] = deviceName;
|
||||
data[PREVIOUS_DEVICE] = previousDevice.deviceName();
|
||||
data[WAS_DEFAULT] = wasDefault;
|
||||
|
||||
UserActivityLogger::getInstance().logAction("selected_audio_device", data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,4 +263,4 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceI
|
|||
static std::once_flag outputFlag;
|
||||
std::call_once(outputFlag, initialize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,8 @@ public:
|
|||
void resetDevice(bool contextIsHMD, const QString& device);
|
||||
|
||||
signals:
|
||||
void deviceSelected(const QAudioDeviceInfo& device);
|
||||
void deviceSelected(const QAudioDeviceInfo& device = QAudioDeviceInfo(),
|
||||
const QAudioDeviceInfo& previousDevice = QAudioDeviceInfo());
|
||||
void deviceChanged(const QAudioDeviceInfo& device);
|
||||
|
||||
private slots:
|
||||
|
@ -53,6 +54,8 @@ private slots:
|
|||
private:
|
||||
friend class AudioDevices;
|
||||
|
||||
bool setDevice(int index, bool fromUser);
|
||||
|
||||
static QHash<int, QByteArray> _roles;
|
||||
static Qt::ItemFlags _flags;
|
||||
|
||||
|
@ -76,8 +79,7 @@ signals:
|
|||
|
||||
private slots:
|
||||
void onContextChanged(const QString& context);
|
||||
void onInputDeviceSelected(const QAudioDeviceInfo& device);
|
||||
void onOutputDeviceSelected(const QAudioDeviceInfo& device);
|
||||
void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice);
|
||||
void onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device);
|
||||
void onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices);
|
||||
|
||||
|
|
|
@ -257,8 +257,40 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
|||
PacketType headerType = NLPacket::typeInHeader(packet);
|
||||
|
||||
if (NON_SOURCED_PACKETS.contains(headerType)) {
|
||||
emit dataReceived(NodeType::Unassigned, packet.getPayloadSize());
|
||||
return true;
|
||||
if (REPLICATED_PACKET_MAPPING.key(headerType) != PacketType::Unknown) {
|
||||
// this is a replicated packet type - make sure the socket that sent it to us matches
|
||||
// one from one of our current upstream nodes
|
||||
|
||||
NodeType_t sendingNodeType { NodeType::Unassigned };
|
||||
|
||||
eachNodeBreakable([&packet, &sendingNodeType](const SharedNodePointer& node){
|
||||
if (NodeType::isUpstream(node->getType()) && node->getPublicSocket() == packet.getSenderSockAddr()) {
|
||||
sendingNodeType = node->getType();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (sendingNodeType != NodeType::Unassigned) {
|
||||
emit dataReceived(sendingNodeType, packet.getPayloadSize());
|
||||
return true;
|
||||
} else {
|
||||
static const QString UNSOLICITED_REPLICATED_REGEX =
|
||||
"Replicated packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown upstream";
|
||||
static QString repeatedMessage
|
||||
= LogHandler::getInstance().addRepeatedMessageRegex(UNSOLICITED_REPLICATED_REGEX);
|
||||
|
||||
qCDebug(networking) << "Replicated packet of type" << headerType
|
||||
<< "received from unknown upstream" << packet.getSenderSockAddr();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
emit dataReceived(NodeType::Unassigned, packet.getPayloadSize());
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
QUuid sourceID = NLPacket::sourceIDInHeader(packet);
|
||||
|
||||
|
@ -583,14 +615,14 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
matchingNode->setPermissions(permissions);
|
||||
matchingNode->setConnectionSecret(connectionSecret);
|
||||
matchingNode->setIsReplicated(isReplicated);
|
||||
matchingNode->setIsUpstream(isUpstream);
|
||||
matchingNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType));
|
||||
|
||||
return matchingNode;
|
||||
} else {
|
||||
// we didn't have this node, so add them
|
||||
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket);
|
||||
newNode->setIsReplicated(isReplicated);
|
||||
newNode->setIsUpstream(isUpstream);
|
||||
newNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType));
|
||||
newNode->setConnectionSecret(connectionSecret);
|
||||
newNode->setPermissions(permissions);
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ void NodeType::init() {
|
|||
TypeNameHash.insert(NodeType::MessagesMixer, "Messages Mixer");
|
||||
TypeNameHash.insert(NodeType::AssetServer, "Asset Server");
|
||||
TypeNameHash.insert(NodeType::EntityScriptServer, "Entity Script Server");
|
||||
TypeNameHash.insert(NodeType::UpstreamAudioMixer, "Upstream Audio Mixer");
|
||||
TypeNameHash.insert(NodeType::UpstreamAvatarMixer, "Upstream Avatar Mixer");
|
||||
TypeNameHash.insert(NodeType::DownstreamAudioMixer, "Downstream Audio Mixer");
|
||||
TypeNameHash.insert(NodeType::DownstreamAvatarMixer, "Downstream Avatar Mixer");
|
||||
TypeNameHash.insert(NodeType::Unassigned, "Unassigned");
|
||||
|
@ -52,8 +54,23 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
|
|||
return matchedTypeName != TypeNameHash.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME;
|
||||
}
|
||||
|
||||
bool NodeType::isUpstream(NodeType_t nodeType) {
|
||||
return nodeType == NodeType::UpstreamAudioMixer || nodeType == NodeType::UpstreamAvatarMixer;
|
||||
}
|
||||
|
||||
bool NodeType::isDownstream(NodeType_t nodeType) {
|
||||
return nodeType == NodeType::DownstreamAudioMixer || nodeType == NodeType::DownstreamAvatarMixer;
|
||||
return nodeType == NodeType::DownstreamAudioMixer || nodeType == NodeType::DownstreamAvatarMixer;
|
||||
}
|
||||
|
||||
NodeType_t NodeType::upstreamType(NodeType_t primaryType) {
|
||||
switch (primaryType) {
|
||||
case AudioMixer:
|
||||
return UpstreamAudioMixer;
|
||||
case AvatarMixer:
|
||||
return UpstreamAvatarMixer;
|
||||
default:
|
||||
return Unassigned;
|
||||
}
|
||||
}
|
||||
|
||||
NodeType_t NodeType::downstreamType(NodeType_t primaryType) {
|
||||
|
|
|
@ -669,9 +669,11 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
|||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
|
||||
nodeLocalSocket, isReplicated, false, connectionUUID, permissions);
|
||||
|
||||
// nodes that are downstream of our own type are kept alive when we hear about them from the domain server
|
||||
if (node->getType() == NodeType::downstreamType(_ownerType)) {
|
||||
// nodes that are downstream or upstream of our own type are kept alive when we hear about them from the domain server
|
||||
// and always have their public socket as their active socket
|
||||
if (node->getType() == NodeType::downstreamType(_ownerType) || node->getType() == NodeType::upstreamType(_ownerType)) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ namespace NodeType {
|
|||
const NodeType_t AssetServer = 'A';
|
||||
const NodeType_t MessagesMixer = 'm';
|
||||
const NodeType_t EntityScriptServer = 'S';
|
||||
const NodeType_t UpstreamAudioMixer = 'B';
|
||||
const NodeType_t UpstreamAvatarMixer = 'C';
|
||||
const NodeType_t DownstreamAudioMixer = 'a';
|
||||
const NodeType_t DownstreamAvatarMixer = 'w';
|
||||
const NodeType_t Unassigned = 1;
|
||||
|
@ -32,9 +34,12 @@ namespace NodeType {
|
|||
void init();
|
||||
|
||||
const QString& getNodeTypeName(NodeType_t nodeType);
|
||||
bool isUpstream(NodeType_t nodeType);
|
||||
bool isDownstream(NodeType_t nodeType);
|
||||
NodeType_t upstreamType(NodeType_t primaryType);
|
||||
NodeType_t downstreamType(NodeType_t primaryType);
|
||||
|
||||
|
||||
NodeType_t fromString(QString type);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ const QHash<PacketType, PacketType> REPLICATED_PACKET_MAPPING {
|
|||
{ PacketType::SilentAudioFrame, PacketType::ReplicatedSilentAudioFrame },
|
||||
{ PacketType::AvatarIdentity, PacketType::ReplicatedAvatarIdentity },
|
||||
{ PacketType::KillAvatar, PacketType::ReplicatedKillAvatar },
|
||||
{ PacketType::BulkAvatarData, PacketType::ReplicatedBulkAvatarData }
|
||||
};
|
||||
|
||||
PacketVersion versionForPacketType(PacketType packetType) {
|
||||
|
|
|
@ -172,7 +172,7 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const
|
|||
emit unhandledException(exception);
|
||||
}
|
||||
}, Qt::DirectConnection);
|
||||
|
||||
|
||||
setProcessEventsInterval(MSECS_PER_SECOND);
|
||||
if (isEntityServerScript()) {
|
||||
qCDebug(scriptengine) << "isEntityServerScript() -- limiting maxRetries to 1";
|
||||
|
@ -281,7 +281,7 @@ void ScriptEngine::runDebuggable() {
|
|||
scriptDebugMenu = nullptr;
|
||||
}
|
||||
}
|
||||
disconnect(timer);
|
||||
disconnect(timer);
|
||||
});
|
||||
|
||||
connect(timer, &QTimer::timeout, [this, timer] {
|
||||
|
@ -340,7 +340,7 @@ void ScriptEngine::runInThread() {
|
|||
QThread* workerThread = new QThread();
|
||||
workerThread->setObjectName(QString("js:") + getFilename().replace("about:",""));
|
||||
moveToThread(workerThread);
|
||||
|
||||
|
||||
// NOTE: If you connect any essential signals for proper shutdown or cleanup of
|
||||
// the script engine, make sure to add code to "reconnect" them to the
|
||||
// disconnectNonEssentialSignals() method
|
||||
|
@ -1047,26 +1047,26 @@ void ScriptEngine::run() {
|
|||
auto beforeSleep = clock::now();
|
||||
|
||||
// Throttle to SCRIPT_FPS
|
||||
// We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will
|
||||
// We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will
|
||||
// calculate a sleepUntil to be the time from our start time until the original target
|
||||
// sleepUntil for this frame. This approach will allow us to "catch up" in the event
|
||||
// that some of our script udpates/frames take a little bit longer than the target average
|
||||
// sleepUntil for this frame. This approach will allow us to "catch up" in the event
|
||||
// that some of our script udpates/frames take a little bit longer than the target average
|
||||
// to execute.
|
||||
// NOTE: if we go to variable SCRIPT_FPS, then we will need to reconsider this approach
|
||||
const std::chrono::microseconds TARGET_SCRIPT_FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1);
|
||||
clock::time_point targetSleepUntil(startTime + (thisFrame++ * TARGET_SCRIPT_FRAME_DURATION));
|
||||
|
||||
// However, if our sleepUntil is not at least our average update and timer execution time
|
||||
// into the future it means our script is taking too long in its updates, and we want to
|
||||
// punish the script a little bit. So we will force the sleepUntil to be at least our
|
||||
// However, if our sleepUntil is not at least our average update and timer execution time
|
||||
// into the future it means our script is taking too long in its updates, and we want to
|
||||
// punish the script a little bit. So we will force the sleepUntil to be at least our
|
||||
// averageUpdate + averageTimerPerFrame time into the future.
|
||||
auto averageUpdate = totalUpdates / thisFrame;
|
||||
auto averageTimerPerFrame = _totalTimerExecution / thisFrame;
|
||||
auto averageTimerAndUpdate = averageUpdate + averageTimerPerFrame;
|
||||
auto sleepUntil = std::max(targetSleepUntil, beforeSleep + averageTimerAndUpdate);
|
||||
|
||||
// We don't want to actually sleep for too long, because it causes our scripts to hang
|
||||
// on shutdown and stop... so we want to loop and sleep until we've spent our time in
|
||||
// We don't want to actually sleep for too long, because it causes our scripts to hang
|
||||
// on shutdown and stop... so we want to loop and sleep until we've spent our time in
|
||||
// purgatory, constantly checking to see if our script was asked to end
|
||||
bool processedEvents = false;
|
||||
while (!_isFinished && clock::now() < sleepUntil) {
|
||||
|
@ -1399,7 +1399,7 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re
|
|||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return QString();
|
||||
}
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
QUrl url(moduleId);
|
||||
|
||||
auto displayId = moduleId;
|
||||
|
@ -1465,7 +1465,7 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re
|
|||
canonical.setPath(file.canonicalFilePath());
|
||||
}
|
||||
|
||||
bool disallowOutsideFiles = !defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile();
|
||||
bool disallowOutsideFiles = !PathUtils::defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile();
|
||||
if (disallowOutsideFiles && !PathUtils::isDescendantOf(canonical, currentSandboxURL)) {
|
||||
return throwResolveError(makeError(message.arg(
|
||||
QString("path '%1' outside of origin script '%2' '%3'")
|
||||
|
@ -1750,7 +1750,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
return;
|
||||
}
|
||||
if (DependencyManager::get<ScriptEngines>()->isStopped()) {
|
||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:"
|
||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:"
|
||||
+ includeFiles.join(",") + "parent script:" + getFilename());
|
||||
return; // bail early
|
||||
}
|
||||
|
@ -1762,7 +1762,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
bool isStandardLibrary = false;
|
||||
if (file.startsWith("/~/")) {
|
||||
thisURL = expandScriptUrl(QUrl::fromLocalFile(expandScriptPath(file)));
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
if (!defaultScriptsLoc.isParentOf(thisURL)) {
|
||||
scriptWarningMessage("Script.include() -- skipping" + file + "-- outside of standard libraries");
|
||||
continue;
|
||||
|
@ -1774,7 +1774,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
|
||||
bool disallowOutsideFiles = thisURL.isLocalFile() && !isStandardLibrary && !currentSandboxURL.isLocalFile();
|
||||
if (disallowOutsideFiles && !PathUtils::isDescendantOf(thisURL, currentSandboxURL)) {
|
||||
scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString()
|
||||
scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString()
|
||||
+ "outside of original entity script" + currentSandboxURL.toString());
|
||||
} else {
|
||||
// We could also check here for CORS, but we don't yet.
|
||||
|
@ -1844,7 +1844,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
|
||||
void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
|
||||
if (DependencyManager::get<ScriptEngines>()->isStopped()) {
|
||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
|
||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
|
||||
+ includeFile + "parent script:" + getFilename());
|
||||
return; // bail early
|
||||
}
|
||||
|
@ -1862,12 +1862,12 @@ void ScriptEngine::load(const QString& loadFile) {
|
|||
return;
|
||||
}
|
||||
if (DependencyManager::get<ScriptEngines>()->isStopped()) {
|
||||
scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:"
|
||||
scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:"
|
||||
+ loadFile + "parent script:" + getFilename());
|
||||
return; // bail early
|
||||
}
|
||||
if (!currentEntityIdentifier.isInvalidID()) {
|
||||
scriptWarningMessage("Script.load() from entity script is ignored... loadFile:"
|
||||
scriptWarningMessage("Script.load() from entity script is ignored... loadFile:"
|
||||
+ loadFile + "parent script:" + getFilename() + "entity: " + currentEntityIdentifier.toString());
|
||||
return; // bail early
|
||||
}
|
||||
|
@ -2548,7 +2548,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
|||
qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
|
||||
"entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
|
||||
#endif
|
||||
|
||||
|
||||
if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
|
||||
refreshFileScript(entityID);
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ ScriptEngines::ScriptEngines(ScriptEngine::Context context)
|
|||
QUrl normalizeScriptURL(const QUrl& rawScriptURL) {
|
||||
if (rawScriptURL.scheme() == "file") {
|
||||
QUrl fullNormal = rawScriptURL;
|
||||
QUrl defaultScriptLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptLoc = PathUtils::defaultScriptsLocation();
|
||||
|
||||
// if this url is something "beneath" the default script url, replace the local path with ~
|
||||
if (fullNormal.scheme() == defaultScriptLoc.scheme() &&
|
||||
|
@ -93,7 +93,7 @@ QUrl normalizeScriptURL(const QUrl& rawScriptURL) {
|
|||
|
||||
QString expandScriptPath(const QString& rawPath) {
|
||||
QStringList splitPath = rawPath.split("/");
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
return defaultScriptsLoc.path() + "/" + splitPath.mid(2).join("/"); // 2 to skip the slashes in /~/
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) {
|
|||
QFileInfo fileInfo(url.toLocalFile());
|
||||
url = QUrl::fromLocalFile(fileInfo.canonicalFilePath());
|
||||
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
if (!defaultScriptsLoc.isParentOf(url)) {
|
||||
qCWarning(scriptengine) << "Script.include() ignoring file path" << rawScriptURL
|
||||
<< "-- outside of standard libraries: "
|
||||
|
@ -327,6 +327,13 @@ void ScriptEngines::saveScripts() {
|
|||
return;
|
||||
}
|
||||
|
||||
// don't save scripts if we started with --scripts, as we would overwrite
|
||||
// the scripts that the user expects to be there when launched without the
|
||||
// --scripts override.
|
||||
if (_defaultScriptsLocationOverridden) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Saves all currently running user-loaded scripts
|
||||
QVariantList list;
|
||||
|
||||
|
@ -541,11 +548,11 @@ void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) {
|
|||
initializer(scriptEngine);
|
||||
}
|
||||
|
||||
// FIXME disabling 'shift key' debugging for now. If you start up the application with
|
||||
// the shift key held down, it triggers a deadlock because of script interfaces running
|
||||
// FIXME disabling 'shift key' debugging for now. If you start up the application with
|
||||
// the shift key held down, it triggers a deadlock because of script interfaces running
|
||||
// on the main thread
|
||||
auto const wantDebug = scriptEngine->isDebuggable(); // || (qApp->queryKeyboardModifiers() & Qt::ShiftModifier);
|
||||
|
||||
|
||||
if (HIFI_SCRIPT_DEBUGGABLES && wantDebug) {
|
||||
scriptEngine->runDebuggable();
|
||||
} else {
|
||||
|
@ -581,5 +588,5 @@ void ScriptEngines::onScriptEngineError(const QString& scriptFilename) {
|
|||
}
|
||||
|
||||
QString ScriptEngines::getDefaultScriptsLocation() const {
|
||||
return defaultScriptsLocation().toString();
|
||||
return PathUtils::defaultScriptsLocation().toString();
|
||||
}
|
||||
|
|
|
@ -66,6 +66,8 @@ public:
|
|||
|
||||
Q_PROPERTY(QString defaultScriptsPath READ getDefaultScriptsLocation)
|
||||
|
||||
void defaultScriptsLocationOverridden(bool overridden) { _defaultScriptsLocationOverridden = overridden; };
|
||||
|
||||
// Called at shutdown time
|
||||
void shutdownScripting();
|
||||
bool isStopped() const { return _isStopped; }
|
||||
|
@ -113,6 +115,7 @@ protected:
|
|||
ScriptsModelFilter _scriptsModelFilter;
|
||||
std::atomic<bool> _isStopped { false };
|
||||
std::atomic<bool> _isReloading { false };
|
||||
bool _defaultScriptsLocationOverridden { false };
|
||||
};
|
||||
|
||||
QUrl normalizeScriptURL(const QUrl& rawScriptURL);
|
||||
|
|
|
@ -125,15 +125,15 @@ int ScriptsModel::columnCount(const QModelIndex& parent) const {
|
|||
|
||||
void ScriptsModel::updateScriptsLocation(const QString& newPath) {
|
||||
_fsWatcher.removePath(_localDirectory.absolutePath());
|
||||
|
||||
|
||||
if (!newPath.isEmpty()) {
|
||||
_localDirectory.setPath(newPath);
|
||||
|
||||
|
||||
if (!_localDirectory.absolutePath().isEmpty()) {
|
||||
_fsWatcher.addPath(_localDirectory.absolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
reloadLocalFiles();
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ void ScriptsModel::reloadDefaultFiles() {
|
|||
}
|
||||
|
||||
void ScriptsModel::requestDefaultFiles(QString marker) {
|
||||
QUrl url(defaultScriptsLocation());
|
||||
QUrl url(PathUtils::defaultScriptsLocation());
|
||||
|
||||
// targets that don't have a scripts folder in the appropriate location will have an empty URL here
|
||||
if (!url.isEmpty()) {
|
||||
|
@ -244,7 +244,7 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) {
|
|||
lastKey = xml.text().toString();
|
||||
if (jsRegex.exactMatch(xml.text().toString())) {
|
||||
QString localPath = lastKey.split("/").mid(1).join("/");
|
||||
QUrl fullPath = defaultScriptsLocation();
|
||||
QUrl fullPath = PathUtils::defaultScriptsLocation();
|
||||
fullPath.setPath(fullPath.path() + lastKey);
|
||||
const QString fullPathStr = normalizeScriptURL(fullPath).toString();
|
||||
_treeNodes.append(new TreeNodeScript(localPath, fullPathStr, SCRIPT_ORIGIN_DEFAULT));
|
||||
|
|
|
@ -71,21 +71,33 @@ QString findMostRecentFileExtension(const QString& originalFileName, QVector<QSt
|
|||
return newestFileName;
|
||||
}
|
||||
|
||||
QUrl defaultScriptsLocation() {
|
||||
// return "http://s3.amazonaws.com/hifi-public";
|
||||
#ifdef Q_OS_WIN
|
||||
QString path = QCoreApplication::applicationDirPath() + "/scripts";
|
||||
#elif defined(Q_OS_OSX)
|
||||
QString path = QCoreApplication::applicationDirPath() + "/../Resources/scripts";
|
||||
#else
|
||||
QString path = QCoreApplication::applicationDirPath() + "/scripts";
|
||||
#endif
|
||||
QUrl PathUtils::defaultScriptsLocation(const QString& newDefaultPath) {
|
||||
static QString overriddenDefaultScriptsLocation = "";
|
||||
QString path;
|
||||
|
||||
// set overriddenDefaultScriptLocation if it was passed in
|
||||
if (!newDefaultPath.isEmpty()) {
|
||||
overriddenDefaultScriptsLocation = newDefaultPath;
|
||||
}
|
||||
|
||||
// use the overridden location if it is set
|
||||
if (!overriddenDefaultScriptsLocation.isEmpty()) {
|
||||
path = overriddenDefaultScriptsLocation;
|
||||
} else {
|
||||
#ifdef Q_OS_WIN
|
||||
path = QCoreApplication::applicationDirPath() + "/scripts";
|
||||
#elif defined(Q_OS_OSX)
|
||||
path = QCoreApplication::applicationDirPath() + "/../Resources/scripts";
|
||||
#else
|
||||
path = QCoreApplication::applicationDirPath() + "/scripts";
|
||||
#endif
|
||||
}
|
||||
|
||||
// turn the string into a legit QUrl
|
||||
QFileInfo fileInfo(path);
|
||||
return QUrl::fromLocalFile(fileInfo.canonicalFilePath());
|
||||
}
|
||||
|
||||
|
||||
QString PathUtils::stripFilename(const QUrl& url) {
|
||||
// Guard against meaningless query and fragment parts.
|
||||
// Do NOT use PreferLocalFile as its behavior is unpredictable (e.g., on defaultScriptsLocation())
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_PathUtils_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
|
||||
/**jsdoc
|
||||
|
@ -38,11 +37,11 @@ public:
|
|||
static QString stripFilename(const QUrl& url);
|
||||
// note: this is FS-case-sensitive version of parentURL.isParentOf(childURL)
|
||||
static bool isDescendantOf(const QUrl& childURL, const QUrl& parentURL);
|
||||
static QUrl defaultScriptsLocation(const QString& newDefault = "");
|
||||
|
||||
};
|
||||
|
||||
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions);
|
||||
QString findMostRecentFileExtension(const QString& originalFileName, QVector<QString> possibleExtensions);
|
||||
|
||||
QUrl defaultScriptsLocation();
|
||||
|
||||
#endif // hifi_PathUtils_h
|
||||
|
|
Loading…
Reference in a new issue