Merge pull request #12815 from AndrewMeadows/workload-010

workload: add/remove entities to physics simulation according to workload boundaries
This commit is contained in:
Sam Gateau 2018-04-06 11:30:36 -07:00 committed by GitHub
commit 876082713e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 144 additions and 112 deletions

View file

@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC66.zip
URL_MD5 91edfde96e06efc847ca327ab97f4c74
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC66-v2.zip
URL_MD5 d76bdb3e2bf7ae5d20115bd97b0c44a8
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -122,9 +122,21 @@ Item {
newViewRequestedCallback(request)
}
// Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false
// as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns
// loading true, and never turns it false again. safeLoading provides a workaround, but it should be removed
// when QT fixes this.
property bool safeLoading: false
property bool loadingLatched: false
property var loadingRequest: null
onLoadingChanged: {
flick.onLoadingChanged(loadRequest)
loadingChangedCallback(loadRequest)
webViewCore.loadingRequest = loadRequest;
webViewCore.safeLoading = webViewCore.loading && !loadingLatched;
webViewCore.loadingLatched |= webViewCore.loading;
}
onSafeLoadingChanged: {
flick.onLoadingChanged(webViewCore.loadingRequest)
loadingChangedCallback(webViewCore.loadingRequest)
}
}
@ -133,7 +145,7 @@ Item {
x: flick.width/2 - width/2
y: flick.height/2 - height/2
source: "../../icons/loader-snake-64-w.gif"
visible: webViewCore.loading && /^(http.*|)$/i.test(webViewCore.url.toString())
visible: webViewCore.safeLoading && /^(http.*|)$/i.test(webViewCore.url.toString())
playing: visible
z: 10000
}

View file

@ -3049,7 +3049,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location";
static const QString SENT_TO_ENTRY = "entry";
static const QString SENT_TO_SANDBOX = "sandbox";
QString sentTo;
@ -3058,15 +3057,8 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
#if !defined(Q_OS_ANDROID)
showHelp();
#endif
if (sandboxIsRunning) {
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
DependencyManager::get<AddressManager>()->goToLocalSandbox();
sentTo = SENT_TO_SANDBOX;
} else {
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
}
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
firstRun.set(false);
} else {
@ -4705,6 +4697,7 @@ void Application::init() {
}, Qt::QueuedConnection);
_gameWorkload.startup(getEntities()->getWorkloadSpace(), _main3DScene, _entitySimulation);
_entitySimulation->setWorkloadSpace(getEntities()->getWorkloadSpace());
}
void Application::updateLOD(float deltaTime) const {

View file

@ -129,7 +129,7 @@ void DiscoverabilityManager::updateLocation() {
// Update Steam
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
steamClient->updateLocation(domainHandler.getHostname(), addressManager->currentFacingShareableAddress());
steamClient->updateLocation(domainHandler.getHostname(), addressManager->currentFacingPublicAddress());
}
}

View file

@ -95,7 +95,7 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename) {
// adding URL to snapshot
QUrl currentURL = DependencyManager::get<AddressManager>()->currentShareableAddress();
QUrl currentURL = DependencyManager::get<AddressManager>()->currentPublicAddress();
shot.setText(URL, currentURL.toString());
QString username = DependencyManager::get<AccountManager>()->getAccountInfo().getUsername();

View file

@ -65,7 +65,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) {
} else {
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(true, contents);
delete this;
this->deleteLater();
}
}
@ -75,23 +75,27 @@ void SnapshotUploader::uploadFailure(QNetworkReply& reply) {
if (replyString.size() == 0) {
replyString = reply.errorString();
}
replyString = replyString.left(1000); // Only print first 1000 characters of error
qDebug() << "Snapshot upload reply error (truncated):" << replyString;
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(true, replyString); // maybe someday include _inWorldLocation, _filename?
delete this;
this->deleteLater();
}
void SnapshotUploader::createStorySuccess(QNetworkReply& reply) {
QString replyString = reply.readAll();
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(false, replyString);
delete this;
this->deleteLater();
}
void SnapshotUploader::createStoryFailure(QNetworkReply& reply) {
QString replyString = reply.readAll();
qDebug() << "Error " << reply.errorString() << " uploading snapshot " << _pathname << " from " << _inWorldLocation;
qDebug() << "Error " << reply.errorString() << " uploading snapshot story " << _pathname << " from " << _inWorldLocation;
if (replyString.size() == 0) {
replyString = reply.errorString();
}
replyString = replyString.left(1000); // Only print first 1000 characters of error
qDebug() << "Snapshot story upload reply error (truncated):" << replyString;
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(true, replyString);
delete this;
this->deleteLater();
}

View file

@ -0,0 +1,34 @@
//
// PhysicsBoundary.h
//
// Created by Andrew Meadows 2018.04.05
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "PhysicsBoundary.h"
#include <PhysicsLogging.h>
#include <workload/Space.h>
#include "workload/GameWorkload.h"
void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const Inputs& inputs) {
auto space = context->_space;
if (!space) {
return;
}
GameWorkloadContext* gameContext = static_cast<GameWorkloadContext*>(context.get());
PhysicalEntitySimulationPointer simulation = gameContext->_simulation;
const auto& regionChanges = inputs.get0();
for (uint32_t i = 0; i < (uint32_t)regionChanges.size(); ++i) {
const workload::Space::Change& change = regionChanges[i];
auto entity = space->getOwner(change.proxyId).get<EntityItemPointer>();
if (entity) {
simulation->changeEntity(entity);
qCDebug(physics) << change.proxyId << " : " << "'" << entity->getName() << "' " << (uint32_t)(change.prevRegion) << " --> " << (uint32_t)(change.region);
}
}
}

View file

@ -1,7 +1,7 @@
//
// PhysicsBoundary.h
//
// Created by Sam Gateau on 2/16/2018.
// Created by Andrew Meadows 2018.04.05
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -10,26 +10,10 @@
#ifndef hifi_PhysicsGatekeeper_h
#define hifi_PhysicsGatekeeper_h
#include "workload/Space.h"
#include "workload/Engine.h"
#include "render/Scene.h"
/*
class FooJobConfig : public workload::Job::Config {
Q_OBJECT
Q_PROPERTY(bool fubar MEMBER fubar NOTIFY dirty)
public:
bool fubar{ false };
signals:
void dirty();
protected:
};
*/
#include <iostream> // adebug
#include <workload/RegionTracker.h>
#include <EntityItem.h>
#include <workload/Engine.h>
#include <workload/RegionTracker.h>
#include "PhysicalEntitySimulation.h"
class PhysicsBoundary {
@ -40,51 +24,8 @@ public:
using JobModel = workload::Job::ModelI<PhysicsBoundary, Inputs, Config>; // this doesn't work
PhysicsBoundary() {}
void configure(const Config& config) {
}
void run(const workload::WorkloadContextPointer& context, const Inputs& inputs) {
const auto& regionChanges = inputs.get1();
auto space = context->_space;
if (!space) {
return;
}
uint32_t listSize = (uint32_t)regionChanges.size();
uint32_t totalTransitions = 0;
for (uint32_t i = 0; i < listSize; ++i) {
totalTransitions += (uint32_t)regionChanges[i].size();
}
// we're interested in things entering/leaving R3
uint32_t regionIndex = workload::Region::R3;
uint32_t exitIndex = 2 * regionIndex;
uint32_t numExits = (uint32_t)regionChanges[exitIndex].size();
for (uint32_t i = 0; i < numExits; ++i) {
int32_t proxyID = regionChanges[exitIndex][i];
void* owner = space->getOwner(proxyID).get();
if (owner) {
EntityItem* entity = static_cast<EntityItem*>(owner);
std::cout << "adebug - "
//<< owner
<< " '" << entity->getName().toStdString() << "'"
<< std::endl; // adebug
}
}
uint32_t enterIndex = exitIndex + 1;
uint32_t numEntries = (uint32_t)regionChanges[enterIndex].size();
for (uint32_t i = 0; i < numEntries; ++i) {
int32_t proxyID = regionChanges[enterIndex][i];
void* owner = space->getOwner(proxyID).get();
if (owner) {
EntityItem* entity = static_cast<EntityItem*>(owner);
std::cout << "adebug + "
//<< owner
<< " '" << entity->getName().toStdString() << "'"
<< std::endl; // adebug
}
}
}
void configure(const Config& config) { }
void run(const workload::WorkloadContextPointer& context, const Inputs& inputs);
};
#endif // hifi_PhysicsGatekeeper_h

View file

@ -284,7 +284,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
auto spaceIndex = _space->allocateID();
workload::Sphere sphere(entity->getWorldPosition(), entity->getBoundingRadius());
workload::Transaction transaction;
transaction.reset(spaceIndex, sphere, workload::Owner(entity.get()));
transaction.reset(spaceIndex, sphere, workload::Owner(entity));
_space->enqueueTransaction(transaction);
entity->setSpaceIndex(spaceIndex);
connect(entity.get(), &EntityItem::spaceUpdate, this, &EntityTreeRenderer::handleSpaceUpdate, Qt::QueuedConnection);

View file

@ -121,7 +121,7 @@ public:
static bool shouldRenderDebugHulls() { return _renderDebugHullsOperator(); }
// Access the workload Space
const workload::SpacePointer getWorkloadSpace() const { return _space; }
workload::SpacePointer getWorkloadSpace() const { return _space; }
signals:
void enterEntity(const EntityItemID& entityItemID);

View file

@ -77,6 +77,17 @@ QUrl AddressManager::currentShareableAddress(bool domainOnly) const {
}
}
QUrl AddressManager::currentPublicAddress(bool domainOnly) const {
// return an address that can be used by others to visit this client's current location. If
// in a serverless domain (which can't be visited) return an empty URL.
QUrl shareableAddress = currentShareableAddress(domainOnly);
if (shareableAddress.scheme() != URL_SCHEME_HIFI) {
return QUrl(); // file: urls aren't public
}
return shareableAddress;
}
QUrl AddressManager::currentFacingShareableAddress() const {
auto hifiURL = currentShareableAddress();
if (hifiURL.scheme() == URL_SCHEME_HIFI) {
@ -86,6 +97,17 @@ QUrl AddressManager::currentFacingShareableAddress() const {
return hifiURL;
}
QUrl AddressManager::currentFacingPublicAddress() const {
// return an address that can be used by others to visit this client's current location. If
// in a serverless domain (which can't be visited) return an empty URL.
QUrl shareableAddress = currentFacingShareableAddress();
if (shareableAddress.scheme() != URL_SCHEME_HIFI) {
return QUrl(); // file: urls aren't public
}
return shareableAddress;
}
void AddressManager::loadSettings(const QString& lookupString) {
#if defined(USE_GLES) && defined(Q_OS_WIN)
handleUrl(QUrl("hifi://127.0.0.0"), LookupTrigger::StartupFromSettings);
@ -288,6 +310,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
// lookupUrl.scheme() == URL_SCHEME_HTTP ||
// lookupUrl.scheme() == URL_SCHEME_HTTPS ||
_previousLookup.clear();
_shareablePlaceName.clear();
QUrl domainURL = PathUtils::expandToLocalDataAbsolutePath(lookupUrl);
setDomainInfo(domainURL, trigger);
emit lookupResultsFinished();

View file

@ -150,7 +150,9 @@ public:
QUrl currentAddress(bool domainOnly = false) const;
QUrl currentFacingAddress() const;
QUrl currentShareableAddress(bool domainOnly = false) const;
QUrl currentPublicAddress(bool domainOnly = false) const;
QUrl currentFacingShareableAddress() const;
QUrl currentFacingPublicAddress() const;
QString currentPath(bool withOrientation = true) const;
QString currentFacingPath() const;

View file

@ -192,17 +192,12 @@ bool OctreePersistThread::process() {
QString lockFileName = _filename + ".lock";
std::ifstream lockFile(qPrintable(lockFileName), std::ios::in | std::ios::binary | std::ios::ate);
if (lockFile.is_open()) {
qCDebug(octree) << "WARNING: Octree lock file detected at startup:" << lockFileName
<< "-- Attempting to restore from previous backup file.";
// This is where we should attempt to find the most recent backup and restore from
// that file as our persist file.
restoreFromMostRecentBackup();
qCDebug(octree) << "WARNING: Octree lock file detected at startup:" << lockFileName;
lockFile.close();
qCDebug(octree) << "Loading Octree... lock file closed:" << lockFileName;
qCDebug(octree) << "Removing lock file:" << lockFileName;
remove(qPrintable(lockFileName));
qCDebug(octree) << "Loading Octree... lock file removed:" << lockFileName;
qCDebug(octree) << "Lock file removed:" << lockFileName;
}
persistentFileRead = _tree->readFromFile(qPrintable(_filename.toLocal8Bit()));

View file

@ -1,6 +1,6 @@
set(TARGET_NAME physics)
setup_hifi_library()
link_hifi_libraries(shared fbx entities graphics)
link_hifi_libraries(shared task workload fbx entities graphics)
include_hifi_library_headers(networking)
include_hifi_library_headers(gpu)
include_hifi_library_headers(avatars)

View file

@ -120,8 +120,10 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
QMutexLocker lock(&_mutex);
assert(entity);
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
uint8_t region = _space->getRegion(entity->getSpaceIndex());
bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical();
if (motionState) {
if (!entity->shouldBePhysical()) {
if (!shouldBePhysical) {
// the entity should be removed from the physical simulation
_incomingChanges.remove(motionState);
_physicalObjects.remove(motionState);
@ -133,7 +135,7 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
} else {
_incomingChanges.insert(motionState);
}
} else if (entity->shouldBePhysical()) {
} else if (shouldBePhysical) {
// The intent is for this object to be in the PhysicsEngine, but it has no MotionState yet.
// Perhaps it's shape has changed and it can now be added?
_entitiesToAddToPhysics.insert(entity);

View file

@ -19,6 +19,7 @@
#include <EntityItem.h>
#include <EntitySimulation.h>
#include <workload/Space.h>
#include "PhysicsEngine.h"
#include "EntityMotionState.h"
@ -45,6 +46,7 @@ public:
~PhysicalEntitySimulation();
void init(EntityTreePointer tree, PhysicsEnginePointer engine, EntityEditPacketSender* packetSender);
void setWorkloadSpace(const workload::SpacePointer space) { _space = space; }
virtual void addDynamic(EntityDynamicPointer dynamic) override;
virtual void applyDynamicChanges() override;
@ -102,6 +104,7 @@ private:
VectorOfEntityMotionStates _owned;
VectorOfEntityMotionStates _bids;
workload::SpacePointer _space;
uint64_t _nextBidExpiry;
uint32_t _lastStepSendPackets { 0 };
};

View file

@ -72,6 +72,7 @@ uint32_t PhysicsEngine::getNumSubsteps() {
// private
void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
qCDebug(physics) << "templog addObject" << (void*)(motionState); // TODO: remove this
assert(motionState);
btVector3 inertia(0.0f, 0.0f, 0.0f);
@ -193,6 +194,7 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
for (auto object : objects) {
btRigidBody* body = object->getRigidBody();
if (body) {
qCDebug(physics) << "removeObject" << (void*)(body->getMotionState()); // TODO: remove this
removeDynamicsForBody(body);
_dynamicsWorld->removeRigidBody(body);

View file

@ -32,6 +32,7 @@
#include "model_lightmap_fade_vert.h"
#include "model_lightmap_normal_map_fade_vert.h"
#include "model_translucent_vert.h"
#include "model_translucent_normal_map_vert.h"
#include "skin_model_fade_vert.h"
#include "skin_model_normal_map_fade_vert.h"
#include "skin_model_fade_dq_vert.h"
@ -68,11 +69,13 @@
#include "model_lightmap_normal_map_frag.h"
#include "model_translucent_frag.h"
#include "model_translucent_unlit_frag.h"
#include "model_translucent_normal_map_frag.h"
#include "model_lightmap_fade_frag.h"
#include "model_lightmap_normal_map_fade_frag.h"
#include "model_translucent_fade_frag.h"
#include "model_translucent_unlit_fade_frag.h"
#include "model_translucent_normal_map_fade_frag.h"
#include "overlay3D_vert.h"
#include "overlay3D_frag.h"
@ -187,6 +190,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
auto modelLightmapVertex = model_lightmap_vert::getShader();
auto modelLightmapNormalMapVertex = model_lightmap_normal_map_vert::getShader();
auto modelTranslucentVertex = model_translucent_vert::getShader();
auto modelTranslucentNormalMapVertex = model_translucent_normal_map_vert::getShader();
auto modelShadowVertex = model_shadow_vert::getShader();
auto modelLightmapFadeVertex = model_lightmap_fade_vert::getShader();
@ -227,6 +231,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
auto modelNormalMapPixel = model_normal_map_frag::getShader();
auto modelTranslucentPixel = model_translucent_frag::getShader();
auto modelTranslucentUnlitPixel = model_translucent_unlit_frag::getShader();
auto modelTranslucentNormalMapPixel = model_translucent_normal_map_frag::getShader();
auto modelShadowPixel = model_shadow_frag::getShader();
auto modelLightmapPixel = model_lightmap_frag::getShader();
auto modelLightmapNormalMapPixel = model_lightmap_normal_map_frag::getShader();
@ -239,6 +244,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
auto modelShadowFadePixel = model_shadow_fade_frag::getShader();
auto modelTranslucentFadePixel = model_translucent_fade_frag::getShader();
auto modelTranslucentUnlitFadePixel = model_translucent_unlit_fade_frag::getShader();
auto modelTranslucentNormalMapFadePixel = model_translucent_normal_map_fade_frag::getShader();
auto simpleFadePixel = simple_textured_fade_frag::getShader();
auto simpleUnlitFadePixel = simple_textured_unlit_fade_frag::getShader();
auto simpleTranslucentFadePixel = simple_transparent_textured_fade_frag::getShader();
@ -296,7 +302,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
simpleVertex, simpleTranslucentUnlitPixel, nullptr, nullptr);
addPipeline(
Key::Builder().withMaterial().withTranslucent().withTangents(),
modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr);
modelTranslucentNormalMapVertex, modelTranslucentNormalMapPixel, nullptr, nullptr);
addPipeline(
// FIXME: Ignore lightmap for translucents meshpart
Key::Builder().withMaterial().withTranslucent().withLightmap(),
@ -316,7 +322,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
simpleFadeVertex, simpleTranslucentUnlitFadePixel, batchSetter, itemSetter);
addPipeline(
Key::Builder().withMaterial().withTranslucent().withTangents().withFade(),
modelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter);
modelTranslucentNormalMapVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter);
addPipeline(
// FIXME: Ignore lightmap for translucents meshpart
Key::Builder().withMaterial().withTranslucent().withLightmap().withFade(),
@ -358,14 +364,14 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr);
addPipeline(
Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents(),
skinModelNormalMapTranslucentVertex, modelTranslucentPixel, nullptr, nullptr);
skinModelNormalMapTranslucentVertex, modelTranslucentNormalMapPixel, nullptr, nullptr);
// Same thing but with Fade on
addPipeline(
Key::Builder().withMaterial().withSkinned().withTranslucent().withFade(),
skinModelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter);
addPipeline(
Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withFade(),
skinModelNormalMapFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter);
skinModelNormalMapFadeVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter);
// dual quaternion skinned
addPipeline(
@ -388,14 +394,14 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
skinModelTranslucentDualQuatVertex, modelTranslucentPixel, nullptr, nullptr);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withTangents(),
skinModelNormalMapTranslucentDualQuatVertex, modelTranslucentPixel, nullptr, nullptr);
skinModelNormalMapTranslucentDualQuatVertex, modelTranslucentNormalMapPixel, nullptr, nullptr);
// Same thing but with Fade on
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withFade(),
skinModelFadeDualQuatVertex, modelTranslucentFadePixel, batchSetter, itemSetter);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withTangents().withFade(),
skinModelNormalMapFadeDualQuatVertex, modelTranslucentFadePixel, batchSetter, itemSetter);
skinModelNormalMapFadeDualQuatVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter);
// Depth-only
addPipeline(

View file

@ -18,13 +18,25 @@ namespace workload {
class Owner {
public:
Owner() = default;
Owner(void* data) : _data(data) {}
Owner(const Owner& other) = default;
Owner& operator=(const Owner& other) = default;
template <class T> Owner(const T& data) : _concept(std::make_shared<Model<T>>(data)) {}
~Owner() {}
void* get() const { return _data; }
template <class T> const T get() const { return std::static_pointer_cast<const Model<T>>(_concept)->_data; }
protected:
class Concept {
public:
virtual ~Concept() = default;
};
template <class T> class Model : public Concept {
public:
using Data = T;
Data _data;
Model(const Data& data) : _data(data) {}
virtual ~Model() = default;
};
private:
void* _data { nullptr };
std::shared_ptr<Concept> _concept;
};
class Proxy {

View file

@ -127,6 +127,10 @@ const Owner Space::getOwner(int32_t proxyID) const {
return Owner();
}
uint8_t Space::getRegion(int32_t proxyID) const {
return _IDAllocator.checkIndex(proxyID) ? _proxies[proxyID].region : Region::INVALID;
}
void Space::clear() {
std::unique_lock<std::mutex> lock(_proxiesMutex);
_IDAllocator.clear();

View file

@ -49,6 +49,7 @@ public:
uint32_t copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const;
const Owner getOwner(int32_t proxyID) const;
uint8_t getRegion(int32_t proxyID) const;
void clear();
private:

View file

@ -40,7 +40,7 @@
}
function onScreenChanged(type, url) {
onHelpScreen = type === "Web" && url.startsWith(HELP_URL);
onHelpScreen = type === "Web" && (url.indexOf(HELP_URL) === 0);
button.editProperties({ isActive: onHelpScreen });
}

View file

@ -411,8 +411,6 @@ function snapshotUploaded(isError, reply) {
} else {
print('Ignoring snapshotUploaded() callback for stale ' + (isGif ? 'GIF' : 'Still' ) + ' snapshot. Stale story ID:', storyID);
}
} else {
print(reply);
}
isUploadingPrintableStill = false;
}