merge upstream/master into andrew/bispinor

This commit is contained in:
Andrew Meadows 2015-02-25 12:08:02 -08:00
commit d85be49694
16 changed files with 476 additions and 131 deletions

View file

@ -88,8 +88,17 @@ endif ()
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH})
# set our OS X deployment target to
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8)
if (APPLE)
# set our OS X deployment target
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8)
# find the 10.9 SDK path
execute_process(COMMAND xcodebuild -sdk -version OUTPUT_VARIABLE XCODE_SDK_VERSIONS)
string(REGEX MATCH \\/.+MacOSX10.9.sdk OSX_SDK_PATH ${XCODE_SDK_VERSIONS})
# set that as the SDK to use
set(CMAKE_OSX_SYSROOT ${OSX_SDK_PATH})
endif ()
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)

View file

@ -396,6 +396,10 @@ var toolBar = (function () {
return handled;
}
Window.domainChanged.connect(function() {
that.setActive(false);
});
that.cleanup = function () {
toolBar.cleanup();
};

View file

@ -38,11 +38,13 @@
// function onIncomingMessage(user, message) {
// //do stuff here;
// var text = "This is a notification";
// wordWrap(text);
// var wrappedText = wordWrap(text);
// createNotification(wrappedText, NotificationType.SNAPSHOT);
// }
//
// This new function must call wordWrap(text) if the length of message is longer than 42 chars or unknown.
// wordWrap() will format the text to fit the notifications overlay and send it to createNotification(text).
// wordWrap() will format the text to fit the notifications overlay and return it
// after that we will send it to createNotification(text).
// If the message is 42 chars or less you should bypass wordWrap() and call createNotification() directly.
@ -50,12 +52,12 @@
//
// 1. Add a key to the keyPressEvent(key).
// 2. Declare a text string.
// 3. Call createNotifications(text) parsing the text.
// 3. Call createNotifications(text, NotificationType) parsing the text.
// example:
// var welcome;
// if (key.text == "q") { //queries number of users online
// var welcome = "There are " + GlobalServices.onlineUsers.length + " users online now.";
// createNotification(welcome);
// createNotification(welcome, NotificationType.USERS_ONLINE);
// }
Script.include("./libraries/globals.js");
Script.include("./libraries/soundArray.js");
@ -83,6 +85,46 @@ var last_users = GlobalServices.onlineUsers;
var users = [];
var ctrlIsPressed = false;
var ready = true;
var MENU_NAME = 'Tools > Notifications';
var PLAY_NOTIFICATION_SOUNDS_MENU_ITEM = "Play Notification Sounds";
var NOTIFICATION_MENU_ITEM_POST = " Notifications";
var PLAY_NOTIFICATION_SOUNDS_SETTING = "play_notification_sounds";
var PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE = "play_notification_sounds_type_";
var NotificationType = {
UNKNOWN: 0,
USER_JOINS: 1,
USER_LEAVES: 2,
MUTE_TOGGLE: 3,
CHAT_MENTION: 4,
USERS_ONLINE: 5,
SNAPSHOT: 6,
WINDOW_RESIZE: 7,
properties: [
{ text: "User Join" },
{ text: "User Leave" },
{ text: "Mute Toggle" },
{ text: "Chat Mention" },
{ text: "Users Online" },
{ text: "Snapshot" },
{ text: "Window Resize" }
],
getTypeFromMenuItem: function(menuItemName) {
if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) {
return NotificationType.UNKNOWN;
}
var preMenuItemName = menuItemName.substr(0, menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length);
for (type in this.properties) {
if (this.properties[type].text === preMenuItemName) {
return parseInt(type) + 1;
}
}
return NotificationType.UNKNOWN;
},
getMenuString: function(type) {
return this.properties[type - 1].text + NOTIFICATION_MENU_ITEM_POST;
}
};
var randomSounds = new SoundArray({ localOnly: true }, true);
var numberOfSounds = 2;
@ -90,15 +132,6 @@ for (var i = 1; i <= numberOfSounds; i++) {
randomSounds.addSound(HIFI_PUBLIC_BUCKET + "sounds/UI/notification-general" + i + ".raw");
}
// When our script shuts down, we should clean up all of our overlays
function scriptEnding() {
for (i = 0; i < notifications.length; i++) {
Overlays.deleteOverlay(notifications[i]);
Overlays.deleteOverlay(buttons[i]);
}
}
Script.scriptEnding.connect(scriptEnding);
var notifications = [];
var buttons = [];
var times = [];
@ -211,8 +244,6 @@ function notify(notice, button, height) {
positions,
last;
randomSounds.playRandom();
if (isOnHMD) {
// Calculate 3D values from 2D overlay properties.
@ -257,7 +288,7 @@ function notify(notice, button, height) {
}
// This function creates and sizes the overlays
function createNotification(text) {
function createNotification(text, notificationType) {
var count = (text.match(/\n/g) || []).length,
breakPoint = 43.0, // length when new line is added
extraLine = 0,
@ -307,6 +338,12 @@ function createNotification(text) {
alpha: backgroundAlpha
};
if (Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) &&
Menu.isOptionChecked(NotificationType.getMenuString(notificationType)))
{
randomSounds.playRandom();
}
notify(noticeProperties, buttonProperties, height);
}
@ -345,8 +382,7 @@ function stringDivider(str, slotWidth, spaceReplacer) {
// formats string to add newline every 43 chars
function wordWrap(str) {
var result = stringDivider(str, 43.0, "\n");
createNotification(result);
return stringDivider(str, 43.0, "\n");
}
// This fires a notification on window resize
@ -358,7 +394,7 @@ function checkSize() {
windowDimensions = Controller.getViewportDimensions();
overlayLocationX = (windowDimensions.x - (width + 60.0));
buttonLocationX = overlayLocationX + (width - 35.0);
createNotification(windowResize);
createNotification(windowResize, NotificationType.WINDOW_RESIZE);
}
}
@ -442,10 +478,7 @@ var STARTUP_TIMEOUT = 500, // ms
// This reports the number of users online at startup
function reportUsers() {
var welcome;
welcome = "Welcome! There are " + GlobalServices.onlineUsers.length + " users online now.";
createNotification(welcome);
createNotification("Welcome! There are " + GlobalServices.onlineUsers.length + " users online now.", NotificationType.USERS_ONLINE);
}
function finishStartup() {
@ -472,13 +505,13 @@ function onOnlineUsersChanged(users) {
if (!isStartingUp()) { // Skip user notifications at startup.
for (i = 0; i < users.length; i += 1) {
if (last_users.indexOf(users[i]) === -1.0) {
createNotification(users[i] + " has joined");
createNotification(users[i] + " has joined", NotificationType.USER_JOINS);
}
}
for (i = 0; i < last_users.length; i += 1) {
if (users.indexOf(last_users[i]) === -1.0) {
createNotification(last_users[i] + " has left");
createNotification(last_users[i] + " has left", NotificationType.USER_LEAVES);
}
}
}
@ -497,7 +530,7 @@ function onIncomingMessage(user, message) {
thisAlert = user + ": " + myMessage;
if (myMessage.indexOf(alertMe) > -1.0) {
wordWrap(thisAlert);
CreateNotification(wordWrap(thisAlert), NotificationType.CHAT_MENTION);
}
}
@ -506,9 +539,9 @@ function onMuteStateChanged() {
var muteState,
muteString;
muteState = AudioDevice.getMuted() ? "muted" : "unmuted";
muteState = AudioDevice.getMuted() ? "muted" : "unmuted";
muteString = "Microphone is now " + muteState;
createNotification(muteString);
createNotification(muteString, NotificationType.MUTE_TOGGLE);
}
// handles mouse clicks on buttons
@ -551,25 +584,58 @@ function keyPressEvent(key) {
if (key.text === "q") { //queries number of users online
numUsers = GlobalServices.onlineUsers.length;
welcome = "There are " + numUsers + " users online now.";
createNotification(welcome);
createNotification(welcome, NotificationType.USERS_ONLINE);
}
if (key.text === "s") {
if (ctrlIsPressed === true) {
noteString = "Snapshot taken.";
createNotification(noteString);
createNotification(noteString, NotificationType.SNAPSHOT);
}
}
}
function setup() {
Menu.addMenu(MENU_NAME);
var checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING);
checked = checked === '' ? true : checked;
Menu.addMenuItem({
menuName: MENU_NAME,
menuItemName: PLAY_NOTIFICATION_SOUNDS_MENU_ITEM,
isCheckable: true,
isChecked: Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING)
});
Menu.addSeparator(MENU_NAME, "Play sounds for:");
for (type in NotificationType.properties) {
checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + (parseInt(type) + 1));
checked = checked === '' ? true : checked;
Menu.addMenuItem({
menuName: MENU_NAME,
menuItemName: NotificationType.properties[type].text + NOTIFICATION_MENU_ITEM_POST,
isCheckable: true,
isChecked: checked
});
}
}
// When our script shuts down, we should clean up all of our overlays
function scriptEnding() {
var i;
for (i = 0; i < notifications.length; i += 1) {
for (var i = 0; i < notifications.length; i++) {
Overlays.deleteOverlay(notifications[i]);
Overlays.deleteOverlay(buttons[i]);
}
Menu.removeMenu(MENU_NAME);
}
function menuItemEvent(menuItem) {
if (menuItem === PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) {
Settings.setValue(PLAY_NOTIFICATION_SOUNDS_SETTING, Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM));
return;
}
var notificationType = NotificationType.getTypeFromMenuItem(menuItem);
if (notificationType !== notificationType.UNKNOWN) {
Settings.setValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + notificationType, Menu.isOptionChecked(menuItem));
}
}
AudioDevice.muteToggled.connect(onMuteStateChanged);
@ -580,3 +646,6 @@ GlobalServices.incomingMessage.connect(onIncomingMessage);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);
Menu.menuItemEvent.connect(menuItemEvent);
setup();

View file

@ -533,6 +533,10 @@ void Application::aboutToQuit() {
}
void Application::cleanupBeforeQuit() {
_entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
ScriptEngine::stopAllScripts(this); // stop all currently running global scripts
// first stop all timers directly or by invokeMethod
// depending on what thread they run in
locationUpdateTimer->stop();
@ -597,13 +601,11 @@ Application::~Application() {
DependencyManager::destroy<GLCanvas>();
qDebug() << "start destroying ResourceCaches Application::~Application() line:" << __LINE__;
DependencyManager::destroy<AnimationCache>();
DependencyManager::destroy<TextureCache>();
DependencyManager::destroy<GeometryCache>();
DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<SoundCache>();
qDebug() << "done destroying ResourceCaches Application::~Application() line:" << __LINE__;
}
void Application::initializeGL() {
@ -1469,6 +1471,10 @@ void Application::checkFPS() {
void Application::idle() {
PerformanceTimer perfTimer("idle");
if (_aboutToQuit) {
return; // bail early, nothing to do here.
}
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
// details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing
@ -3535,19 +3541,20 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance());
#endif
// TODO: Consider moving some of this functionality into the ScriptEngine class instead. It seems wrong that this
// work is being done in the Application class when really these dependencies are more related to the ScriptEngine's
// implementation
QThread* workerThread = new QThread(this);
workerThread->setObjectName("Script Engine Thread");
QString scriptEngineName = QString("Script Thread:") + scriptEngine->getFilename();
workerThread->setObjectName(scriptEngineName);
// when the worker thread is started, call our engine's run..
connect(workerThread, &QThread::started, scriptEngine, &ScriptEngine::run);
// when the thread is terminated, add both scriptEngine and thread to the deleteLater queue
connect(scriptEngine, SIGNAL(finished(const QString&)), scriptEngine, SLOT(deleteLater()));
connect(scriptEngine, SIGNAL(doneRunning()), scriptEngine, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
// when the application is about to quit, stop our script engine so it unwinds properly
connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop()));
auto nodeList = DependencyManager::get<NodeList>();
connect(nodeList.data(), &NodeList::nodeKilled, scriptEngine, &ScriptEngine::nodeKilled);
@ -3558,7 +3565,12 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
}
ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,
bool loadScriptFromEditor, bool activateMainWindow) {
bool loadScriptFromEditor, bool activateMainWindow) {
if (isAboutToQuit()) {
return NULL;
}
QUrl scriptUrl(scriptFilename);
const QString& scriptURLString = scriptUrl.toString();
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
@ -3749,8 +3761,8 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject
voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString());
}
qDebug() << "Voxel costs are" << satoshisPerVoxel << "per voxel and" << satoshisPerMeterCubed << "per meter cubed";
qDebug() << "Destination wallet UUID for voxel payments is" << voxelWalletUUID;
qDebug() << "Octree edits costs are" << satoshisPerVoxel << "per octree cell and" << satoshisPerMeterCubed << "per meter cubed";
qDebug() << "Destination wallet UUID for edit payments is" << voxelWalletUUID;
}
QString Application::getPreviousScriptLocation() {

View file

@ -18,6 +18,7 @@
#include <QScrollArea>
#include "Application.h"
#include "DomainHandler.h"
#include "MainWindow.h"
#include "Menu.h"
#include "ui/ModelsBrowser.h"
@ -34,6 +35,8 @@ WindowScriptingInterface::WindowScriptingInterface() :
_nonBlockingFormActive(false),
_formResult(QDialog::Rejected)
{
const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
connect(&domainHandler, &DomainHandler::hostnameChanged, this, &WindowScriptingInterface::domainChanged);
}
WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height) {

View file

@ -57,6 +57,7 @@ public slots:
QScriptValue peekNonBlockingFormResult(QScriptValue array);
signals:
void domainChanged(const QString& domainHostname);
void inlineButtonClicked(const QString& name);
void nonBlockingFormClosed();

View file

@ -10,6 +10,8 @@
//
#include "Application.h"
#include "GeometryUtil.h"
#include "PlaneShape.h"
#include "BillboardOverlay.h"
@ -191,19 +193,25 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v
float& distance, BoxFace& face) {
if (_texture) {
glm::quat rotation;
if (_isFacingAvatar) {
// rotate about vertical to face the camera
rotation = Application::getInstance()->getCamera()->getRotation();
rotation *= glm::angleAxis(glm::pi<float>(), glm::vec3(0.0f, 1.0f, 0.0f));
} else {
rotation = _rotation;
}
// Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale.
bool isNull = _fromImage.isNull();
float width = isNull ? _texture->getWidth() : _fromImage.width();
float height = isNull ? _texture->getHeight() : _fromImage.height();
float maxSize = glm::max(width, height);
float x = width / (2.0f * maxSize);
float y = -height / (2.0f * maxSize);
float maxDimension = glm::max(x,y);
float scaledDimension = maxDimension * _scale;
glm::vec3 corner = getCenter() - glm::vec3(scaledDimension, scaledDimension, scaledDimension) ;
AACube myCube(corner, scaledDimension * 2.0f);
return myCube.findRayIntersection(origin, direction, distance, face);
glm::vec2 dimensions = _scale * glm::vec2(width / maxSize, height / maxSize);
return findRayRectangleIntersection(origin, direction, rotation, _position, dimensions);
}
return false;
}

View file

@ -17,6 +17,8 @@
#include <SharedUtil.h>
#include <StreamUtils.h>
#include "GeometryUtil.h"
#include "Planar3DOverlay.h"
const float DEFAULT_SIZE = 1.0f;
@ -93,29 +95,5 @@ QScriptValue Planar3DOverlay::getProperty(const QString& property) {
bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face) {
RayIntersectionInfo rayInfo;
rayInfo._rayStart = origin;
rayInfo._rayDirection = direction;
rayInfo._rayLength = std::numeric_limits<float>::max();
PlaneShape plane;
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
glm::vec3 normal = _rotation * UNROTATED_NORMAL;
plane.setNormal(normal);
plane.setPoint(_position); // the position is definitely a point on our plane
bool intersects = plane.findRayIntersection(rayInfo);
if (intersects) {
distance = rayInfo._hitDistance;
glm::vec3 hitPosition = origin + (distance * direction);
glm::vec3 localHitPosition = glm::inverse(_rotation) * (hitPosition - _position);
glm::vec2 halfDimensions = _dimensions / 2.0f;
intersects = -halfDimensions.x <= localHitPosition.x && localHitPosition.x <= halfDimensions.x
&& -halfDimensions.y <= localHitPosition.y && localHitPosition.y <= halfDimensions.y;
}
return intersects;
return findRayRectangleIntersection(origin, direction, _rotation, _position, _dimensions);
}

View file

@ -59,10 +59,17 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
}
EntityTreeRenderer::~EntityTreeRenderer() {
// NOTE: we don't need to delete _entitiesScriptEngine because it's owned by the application and gets cleaned up
// automatically but we do need to delete our sandbox script engine.
delete _sandboxScriptEngine;
_sandboxScriptEngine = NULL;
// NOTE: we don't need to delete _entitiesScriptEngine because it is registered with the application and has a
// signal tied to call it's deleteLater on doneRunning
if (_sandboxScriptEngine) {
// TODO: consider reworking how _sandboxScriptEngine is managed. It's treated differently than _entitiesScriptEngine
// because we don't call registerScriptEngineWithApplicationServices() for it. This implementation is confusing and
// potentially error prone because it's not a full fledged ScriptEngine that has been fully connected to the
// application. We did this so that scripts that were ill-formed could be evaluated but not execute against the
// application services. But this means it's shutdown behavior is different from other ScriptEngines
delete _sandboxScriptEngine;
_sandboxScriptEngine = NULL;
}
}
void EntityTreeRenderer::clear() {
@ -97,6 +104,11 @@ void EntityTreeRenderer::init() {
connect(entityTree, &EntityTree::changingEntityID, this, &EntityTreeRenderer::changingEntityID);
}
void EntityTreeRenderer::shutdown() {
_shuttingDown = true;
}
QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) {
EntityItem* entity = static_cast<EntityTree*>(_tree)->findEntityByEntityItemID(entityItemID);
return loadEntityScript(entity);
@ -156,6 +168,10 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
if (_shuttingDown) {
return QScriptValue(); // since we're shutting down, we don't load any more scripts
}
if (!entity) {
return QScriptValue(); // no entity...
}
@ -235,7 +251,7 @@ void EntityTreeRenderer::setTree(Octree* newTree) {
}
void EntityTreeRenderer::update() {
if (_tree) {
if (_tree && !_shuttingDown) {
EntityTree* tree = static_cast<EntityTree*>(_tree);
tree->update();
@ -258,7 +274,7 @@ void EntityTreeRenderer::update() {
}
void EntityTreeRenderer::checkEnterLeaveEntities() {
if (_tree) {
if (_tree && !_shuttingDown) {
_tree->lockForWrite(); // so that our scripts can do edits if they want
glm::vec3 avatarPosition = _viewState->getAvatarPosition() / (float) TREE_SCALE;
@ -309,7 +325,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
}
void EntityTreeRenderer::leaveAllEntities() {
if (_tree) {
if (_tree && !_shuttingDown) {
_tree->lockForWrite(); // so that our scripts can do edits if they want
// for all of our previous containing entities, if they are no longer containing then send them a leave event
@ -330,7 +346,7 @@ void EntityTreeRenderer::leaveAllEntities() {
}
}
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {
if (_tree) {
if (_tree && !_shuttingDown) {
Model::startScene(renderSide);
RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, renderSide,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@ -700,9 +716,14 @@ QScriptValueList EntityTreeRenderer::createEntityArgs(const EntityItemID& entity
}
void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
// If we don't have a tree, or we're in the process of shutting down, then don't
// process these events.
if (!_tree || _shuttingDown) {
return;
}
PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent");
PickRay ray = _viewState->computePickRay(event->x(), event->y());
bool precisionPicking = !_dontDoPrecisionPicking;
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
if (rayPickResult.intersects) {
@ -714,7 +735,7 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device
if (entityScript.property("mousePressOnEntity").isValid()) {
entityScript.property("mousePressOnEntity").call(entityScript, entityScriptArgs);
}
_currentClickingOnEntityID = rayPickResult.entityID;
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
if (entityScript.property("clickDownOnEntity").isValid()) {
@ -726,6 +747,11 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device
}
void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
// If we don't have a tree, or we're in the process of shutting down, then don't
// process these events.
if (!_tree || _shuttingDown) {
return;
}
PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent");
PickRay ray = _viewState->computePickRay(event->x(), event->y());
bool precisionPicking = !_dontDoPrecisionPicking;
@ -740,7 +766,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
entityScript.property("mouseReleaseOnEntity").call(entityScript, entityScriptArgs);
}
}
// Even if we're no longer intersecting with an entity, if we started clicking on it, and now
// we're releasing the button, then this is considered a clickOn event
if (!_currentClickingOnEntityID.isInvalidID()) {
@ -752,7 +778,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
currentClickingEntity.property("clickReleaseOnEntity").call(currentClickingEntity, currentClickingEntityArgs);
}
}
// makes it the unknown ID, we just released so we can't be clicking on anything
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID();
_lastMouseEvent = MouseEvent(*event, deviceID);
@ -760,10 +786,15 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
}
void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
// If we don't have a tree, or we're in the process of shutting down, then don't
// process these events.
if (!_tree || _shuttingDown) {
return;
}
PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent");
PickRay ray = _viewState->computePickRay(event->x(), event->y());
bool precisionPicking = false; // for mouse moves we do not do precision picking
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
if (rayPickResult.intersects) {
@ -774,15 +805,15 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
if (entityScript.property("mouseMoveEvent").isValid()) {
entityScript.property("mouseMoveEvent").call(entityScript, entityScriptArgs);
}
//qDebug() << "mouseMoveEvent over entity:" << rayPickResult.entityID;
emit mouseMoveOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
if (entityScript.property("mouseMoveOnEntity").isValid()) {
entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs);
}
// handle the hover logic...
// if we were previously hovering over an entity, and this new entity is not the same as our previous entity
// then we need to send the hover leave.
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
@ -832,7 +863,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
}
}
// Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have
// not yet released the hold then this is still considered a holdingClickOnEntity event
if (!_currentClickingOnEntityID.isInvalidID()) {
@ -850,29 +881,37 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
}
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
checkAndCallUnload(entityID);
if (_tree && !_shuttingDown) {
checkAndCallUnload(entityID);
}
_entityScripts.remove(entityID);
}
void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) {
checkAndCallUnload(entityID);
checkAndCallPreload(entityID);
if (_tree && !_shuttingDown) {
checkAndCallUnload(entityID);
checkAndCallPreload(entityID);
}
}
void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) {
// load the entity script if needed...
QScriptValue entityScript = loadEntityScript(entityID);
if (entityScript.property("preload").isValid()) {
QScriptValueList entityArgs = createEntityArgs(entityID);
entityScript.property("preload").call(entityScript, entityArgs);
if (_tree && !_shuttingDown) {
// load the entity script if needed...
QScriptValue entityScript = loadEntityScript(entityID);
if (entityScript.property("preload").isValid()) {
QScriptValueList entityArgs = createEntityArgs(entityID);
entityScript.property("preload").call(entityScript, entityArgs);
}
}
}
void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) {
QScriptValue entityScript = getPreviouslyLoadedEntityScript(entityID);
if (entityScript.property("unload").isValid()) {
QScriptValueList entityArgs = createEntityArgs(entityID);
entityScript.property("unload").call(entityScript, entityArgs);
if (_tree && !_shuttingDown) {
QScriptValue entityScript = getPreviouslyLoadedEntityScript(entityID);
if (entityScript.property("unload").isValid()) {
QScriptValueList entityArgs = createEntityArgs(entityID);
entityScript.property("unload").call(entityScript, entityArgs);
}
}
}
@ -887,6 +926,11 @@ void EntityTreeRenderer::changingEntityID(const EntityItemID& oldEntityID, const
void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB,
const Collision& collision) {
// If we don't have a tree, or we're in the process of shutting down, then don't
// process these events.
if (!_tree || _shuttingDown) {
return;
}
QScriptValue entityScriptA = loadEntityScript(idA);
if (entityScriptA.property("collisionWithEntity").isValid()) {
QScriptValueList args;

View file

@ -46,6 +46,7 @@ public:
virtual int getBoundaryLevelAdjust() const;
virtual void setTree(Octree* newTree);
void shutdown();
void update();
EntityTree* getTree() { return static_cast<EntityTree*>(_tree); }
@ -154,6 +155,8 @@ private:
bool _displayModelElementProxy;
bool _dontDoPrecisionPicking;
bool _shuttingDown = false;
};
#endif // hifi_EntityTreeRenderer_h

View file

@ -1959,7 +1959,7 @@ HeightfieldNode* HeightfieldNode::fillHeight(const glm::vec3& translation, const
} else {
colorWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE;
colorHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE;
newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF);
newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu);
}
int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE;
int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE;
@ -2185,7 +2185,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons
} else {
colorWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE;
colorHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE;
newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF);
newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu);
}
int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE;
int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE;
@ -2428,7 +2428,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons
}
}
}
bool nextAlphaY = stackDest->getEntryAlpha(y + 1, voxelHeight);
int nextAlphaY = stackDest->getEntryAlpha(y + 1, voxelHeight);
if (nextAlphaY == currentAlpha) {
entryDest->hermiteY = 0;
@ -2447,7 +2447,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons
}
int nextStackZ = (int)stackZ + 1;
if (nextStackZ <= innerStackHeight) {
bool nextAlphaZ = newStackContents.at(nextStackZ * stackWidth + (int)stackX).getEntryAlpha(
int nextAlphaZ = newStackContents.at(nextStackZ * stackWidth + (int)stackX).getEntryAlpha(
y, nextVoxelHeightZ);
if (nextAlphaZ == currentAlpha) {
entryDest->hermiteZ = 0;
@ -2828,7 +2828,7 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) {
_height.reset();
}
if (colorWidth > 0 && colorMaterial) {
QByteArray colorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF);
QByteArray colorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu);
for (int i = 0; i < CHILD_COUNT; i++) {
HeightfieldColorPointer childColor = _children[i]->getColor();
if (!childColor) {
@ -3266,7 +3266,7 @@ HeightfieldNode* HeightfieldNode::subdivide(const QVector<quint16>& heightConten
}
for (int i = 0; i < CHILD_COUNT; i++) {
QVector<quint16> childHeightContents(heightWidth * heightHeight);
QByteArray childColorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF);
QByteArray childColorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu);
QByteArray childMaterialContents(materialWidth * materialHeight, 0);
QVector<StackArray> childStackContents(stackWidth * stackHeight);

View file

@ -41,6 +41,7 @@
#include "MIDIEvent.h"
EntityScriptingInterface ScriptEngine::_entityScriptingInterface;
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
@ -94,8 +95,109 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
_isUserLoaded(false),
_arrayBufferClass(new ArrayBufferClass(this))
{
_allScriptsMutex.lock();
_allKnownScriptEngines.insert(this);
_allScriptsMutex.unlock();
}
ScriptEngine::~ScriptEngine() {
// If we're not already in the middle of stopping all scripts, then we should remove ourselves
// from the list of running scripts. We don't do this if we're in the process of stopping all scripts
// because that method removes scripts from its list as it iterates them
if (!_stoppingAllScripts) {
_allScriptsMutex.lock();
_allKnownScriptEngines.remove(this);
_allScriptsMutex.unlock();
}
}
QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;
QMutex ScriptEngine::_allScriptsMutex;
bool ScriptEngine::_stoppingAllScripts = false;
bool ScriptEngine::_doneRunningThisScript = false;
void ScriptEngine::stopAllScripts(QObject* application) {
_allScriptsMutex.lock();
_stoppingAllScripts = true;
QMutableSetIterator<ScriptEngine*> i(_allKnownScriptEngines);
while (i.hasNext()) {
ScriptEngine* scriptEngine = i.next();
QString scriptName = scriptEngine->getFilename();
// NOTE: typically all script engines are running. But there's at least one known exception to this, the
// "entities sandbox" which is only used to evaluate entities scripts to test their validity before using
// them. We don't need to stop scripts that aren't running.
if (scriptEngine->isRunning()) {
// If the script is running, but still evaluating then we need to wait for its evaluation step to
// complete. After that we can handle the stop process appropriately
if (scriptEngine->evaluatePending()) {
while (scriptEngine->evaluatePending()) {
// This event loop allows any started, but not yet finished evaluate() calls to complete
// we need to let these complete so that we can be guaranteed that the script engine isn't
// in a partially setup state, which can confuse our shutdown unwinding.
QEventLoop loop;
QObject::connect(scriptEngine, &ScriptEngine::evaluationFinished, &loop, &QEventLoop::quit);
loop.exec();
}
}
// We disconnect any script engine signals from the application because we don't want to do any
// extra stopScript/loadScript processing that the Application normally does when scripts start
// and stop. We can safely short circuit this because we know we're in the "quitting" process
scriptEngine->disconnect(application);
// Calling stop on the script engine will set it's internal _isFinished state to true, and result
// in the ScriptEngine gracefully ending it's run() method.
scriptEngine->stop();
// We need to wait for the engine to be done running before we proceed, because we don't
// want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing
// any application state after we leave this stopAllScripts() method
scriptEngine->waitTillDoneRunning();
// If the script is stopped, we can remove it from our set
i.remove();
}
}
_stoppingAllScripts = false;
_allScriptsMutex.unlock();
}
void ScriptEngine::waitTillDoneRunning() {
QString scriptName = getFilename();
// If the script never started running or finished running before we got here, we don't need to wait for it
if (_isRunning) {
_doneRunningThisScript = false; // NOTE: this is static, we serialize our waiting for scripts to finish
// NOTE: waitTillDoneRunning() will be called on the main Application thread, inside of stopAllScripts()
// we want the application thread to continue to process events, because the scripts will likely need to
// marshall messages across to the main thread. For example if they access Settings or Meny in any of their
// shutdown code.
while (!_doneRunningThisScript) {
// process events for the main application thread, allowing invokeMethod calls to pass between threads
QCoreApplication::processEvents();
}
}
}
QString ScriptEngine::getFilename() const {
QStringList fileNameParts = _fileNameString.split("/");
QString lastPart;
if (!fileNameParts.isEmpty()) {
lastPart = fileNameParts.last();
}
return lastPart;
}
void ScriptEngine::setIsAvatar(bool isAvatar) {
_isAvatar = isAvatar;
@ -295,12 +397,18 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func
}
void ScriptEngine::evaluate() {
if (_stoppingAllScripts) {
return; // bail early
}
if (!_isInitialized) {
init();
}
QScriptValue result = evaluate(_scriptContents);
// TODO: why do we check this twice? It seems like the call to clearExcpetions() in the lower level evaluate call
// will cause this code to never actually run...
if (hasUncaughtException()) {
int line = uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString();
@ -310,11 +418,17 @@ void ScriptEngine::evaluate() {
}
QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {
if (_stoppingAllScripts) {
return QScriptValue(); // bail early
}
_evaluatesPending++;
QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);
if (hasUncaughtException()) {
int line = uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString();
}
_evaluatesPending--;
emit evaluationFinished(result, hasUncaughtException());
clearExceptions();
return result;
@ -333,6 +447,9 @@ void ScriptEngine::sendAvatarBillboardPacket() {
}
void ScriptEngine::run() {
// TODO: can we add a short circuit for _stoppingAllScripts here? What does it mean to not start running if
// we're in the process of stopping?
if (!_isInitialized) {
init();
}
@ -341,12 +458,6 @@ void ScriptEngine::run() {
emit runningStateChanged();
QScriptValue result = evaluate(_scriptContents);
if (hasUncaughtException()) {
int line = uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString();
emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + result.toString());
clearExceptions();
}
QElapsedTimer startTime;
startTime.start();
@ -373,7 +484,7 @@ void ScriptEngine::run() {
break;
}
if (_entityScriptingInterface.getEntityPacketSender()->serversExist()) {
if (!_isFinished && _entityScriptingInterface.getEntityPacketSender()->serversExist()) {
// release the queue of edit entity messages.
_entityScriptingInterface.getEntityPacketSender()->releaseQueuedMessages();
@ -383,7 +494,7 @@ void ScriptEngine::run() {
}
}
if (_isAvatar && _avatarData) {
if (!_isFinished && _isAvatar && _avatarData) {
const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * AudioConstants::SAMPLE_RATE)
/ (1000 * 1000)) + 0.5);
@ -493,9 +604,14 @@ void ScriptEngine::run() {
clearExceptions();
}
emit update(deltaTime);
if (!_isFinished) {
emit update(deltaTime);
}
lastUpdate = now;
}
stopAllTimers(); // make sure all our timers are stopped if the script is ending
emit scriptEnding();
// kill the avatar identity timer
@ -520,6 +636,21 @@ void ScriptEngine::run() {
_isRunning = false;
emit runningStateChanged();
emit doneRunning();
_doneRunningThisScript = true;
}
// NOTE: This is private because it must be called on the same thread that created the timers, which is why
// we want to only call it in our own run "shutdown" processing.
void ScriptEngine::stopAllTimers() {
QMutableHashIterator<QTimer*, QScriptValue> i(_timerFunctionMap);
while (i.hasNext()) {
i.next();
QTimer* timer = i.key();
stopTimer(timer);
}
}
void ScriptEngine::stop() {
@ -560,10 +691,20 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int
}
QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) {
if (_stoppingAllScripts) {
qDebug() << "Script.setInterval() while shutting down is ignored... parent script:" << getFilename();
return NULL; // bail early
}
return setupTimerWithInterval(function, intervalMS, false);
}
QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) {
if (_stoppingAllScripts) {
qDebug() << "Script.setTimeout() while shutting down is ignored... parent script:" << getFilename();
return NULL; // bail early
}
return setupTimerWithInterval(function, timeoutMS, true);
}
@ -604,13 +745,16 @@ void ScriptEngine::print(const QString& message) {
emit printedMessage(message);
}
/**
* If a callback is specified, the included files will be loaded asynchronously and the callback will be called
* when all of the files have finished loading.
* If no callback is specified, the included files will be loaded synchronously and will block execution until
* all of the files have finished loading.
*/
// If a callback is specified, the included files will be loaded asynchronously and the callback will be called
// when all of the files have finished loading.
// If no callback is specified, the included files will be loaded synchronously and will block execution until
// all of the files have finished loading.
void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) {
if (_stoppingAllScripts) {
qDebug() << "Script.include() while shutting down is ignored..."
<< "includeFiles:" << includeFiles << "parent script:" << getFilename();
return; // bail early
}
QList<QUrl> urls;
for (QString file : includeFiles) {
urls.append(resolvePath(file));
@ -650,12 +794,27 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
}
void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
if (_stoppingAllScripts) {
qDebug() << "Script.include() while shutting down is ignored... "
<< "includeFile:" << includeFile << "parent script:" << getFilename();
return; // bail early
}
QStringList urls;
urls.append(includeFile);
include(urls, callback);
}
// NOTE: The load() command is similar to the include() command except that it loads the script
// as a stand-alone script. To accomplish this, the ScriptEngine class just emits a signal which
// the Application or other context will connect to in order to know to actually load the script
void ScriptEngine::load(const QString& loadFile) {
if (_stoppingAllScripts) {
qDebug() << "Script.load() while shutting down is ignored... "
<< "loadFile:" << loadFile << "parent script:" << getFilename();
return; // bail early
}
QUrl url = resolvePath(loadFile);
emit loadScript(url.toString(), false);
}

View file

@ -16,6 +16,7 @@
#include <QtCore/QObject>
#include <QtCore/QUrl>
#include <QtCore/QWaitCondition>
#include <QtScript/QScriptEngine>
#include <AnimationCache.h>
@ -43,6 +44,8 @@ public:
const QString& fileNameString = QString(""),
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
~ScriptEngine();
/// Access the EntityScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
static EntityScriptingInterface* getEntityScriptingInterface() { return &_entityScriptingInterface; }
@ -83,11 +86,18 @@ public:
bool isFinished() const { return _isFinished; }
bool isRunning() const { return _isRunning; }
bool evaluatePending() const { return _evaluatesPending > 0; }
void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
bool isUserLoaded() const { return _isUserLoaded; }
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
QString getFilename() const;
static void stopAllScripts(QObject* application);
void waitTillDoneRunning();
public slots:
void loadURL(const QUrl& scriptURL);
@ -118,12 +128,14 @@ signals:
void runningStateChanged();
void evaluationFinished(QScriptValue result, bool isException);
void loadScript(const QString& scriptName, bool isUserLoaded);
void doneRunning();
protected:
QString _scriptContents;
QString _parentURL;
bool _isFinished;
bool _isRunning;
int _evaluatesPending = 0;
bool _isInitialized;
bool _isAvatar;
QTimer* _avatarIdentityTimer;
@ -134,6 +146,7 @@ protected:
int _numAvatarSoundSentBytes;
private:
void stopAllTimers();
void sendAvatarIdentityPacket();
void sendAvatarBillboardPacket();
@ -156,6 +169,12 @@ private:
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
private slots:
void handleScriptDownload();
private:
static QSet<ScriptEngine*> _allKnownScriptEngines;
static QMutex _allScriptsMutex;
static bool _stoppingAllScripts;
static bool _doneRunningThisScript;
};
#endif // hifi_ScriptEngine_h

View file

@ -12,8 +12,10 @@
#include <cstring>
#include <cmath>
#include "SharedUtil.h"
#include "GeometryUtil.h"
#include "PlaneShape.h"
#include "RayIntersectionInfo.h"
#include "SharedUtil.h"
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) {
// compute the projection of the point vector onto the segment vector
@ -491,3 +493,34 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len
}
}
}
bool findRayRectangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::quat& rotation, const glm::vec3& position, const glm::vec2& dimensions) {
RayIntersectionInfo rayInfo;
rayInfo._rayStart = origin;
rayInfo._rayDirection = direction;
rayInfo._rayLength = std::numeric_limits<float>::max();
PlaneShape plane;
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
glm::vec3 normal = rotation * UNROTATED_NORMAL;
plane.setNormal(normal);
plane.setPoint(position); // the position is definitely a point on our plane
bool intersects = plane.findRayIntersection(rayInfo);
if (intersects) {
float distance = rayInfo._hitDistance;
glm::vec3 hitPosition = origin + (distance * direction);
glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position);
glm::vec2 halfDimensions = 0.5f * dimensions;
intersects = -halfDimensions.x <= localHitPosition.x && localHitPosition.x <= halfDimensions.x
&& -halfDimensions.y <= localHitPosition.y && localHitPosition.y <= halfDimensions.y;
}
return intersects;
}

View file

@ -76,6 +76,9 @@ bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& directi
bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& start, const glm::vec3& end, float radius, float& distance);
bool findRayRectangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::quat& rotation, const glm::vec3& position, const glm::vec2& dimensions);
bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance);

View file

@ -8,7 +8,7 @@
#include <iostream>
#ifdef _WINDOWS
#include <winsock2.h>
#include <WS2tcpip.h>
#else
#include <sys/socket.h>
#include <arpa/inet.h>
@ -76,7 +76,7 @@ void runSend(const char* addressOption, int port, int gap, int size, int report)
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(addressOption);
inet_pton(AF_INET, addressOption, &servaddr.sin_addr);
servaddr.sin_port = htons(port);
const int SAMPLES_FOR_SECOND = 1000000 / gap;