mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:24:07 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into blue
This commit is contained in:
commit
23af56c41f
13 changed files with 297 additions and 137 deletions
|
@ -125,6 +125,10 @@ tr.new-row {
|
|||
background-color: #dff0d8;
|
||||
}
|
||||
|
||||
tr.invalid-input {
|
||||
background-color: #f2dede;
|
||||
}
|
||||
|
||||
.graphable-stat {
|
||||
text-align: center;
|
||||
color: #5286BC;
|
||||
|
|
|
@ -38,14 +38,15 @@ var Settings = {
|
|||
DOMAIN_ID_SELECTOR: '[name="metaverse.id"]',
|
||||
ACCESS_TOKEN_SELECTOR: '[name="metaverse.access_token"]',
|
||||
PLACES_TABLE_ID: 'places-table',
|
||||
FORM_ID: 'settings-form'
|
||||
FORM_ID: 'settings-form',
|
||||
INVALID_ROW_CLASS: 'invalid-input'
|
||||
};
|
||||
|
||||
var viewHelpers = {
|
||||
getFormGroup: function(keypath, setting, values, isAdvanced) {
|
||||
form_group = "<div class='form-group " +
|
||||
(isAdvanced ? Settings.ADVANCED_CLASS : "") + " " +
|
||||
(setting.deprecated ? Settings.DEPRECATED_CLASS : "" ) + "' " +
|
||||
(setting.deprecated ? Settings.DEPRECATED_CLASS : "" ) + "' " +
|
||||
"data-keypath='" + keypath + "'>";
|
||||
setting_value = _(values).valueForKeyPath(keypath);
|
||||
|
||||
|
@ -215,8 +216,8 @@ $(document).ready(function(){
|
|||
sibling = sibling.next();
|
||||
}
|
||||
|
||||
if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) {
|
||||
sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click();
|
||||
// for tables with categories we add the entry and setup the new row on enter
|
||||
if (sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).length) {
|
||||
sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).click();
|
||||
|
||||
// set focus to the first input in the new row
|
||||
|
@ -891,48 +892,151 @@ function reloadSettings(callback) {
|
|||
});
|
||||
}
|
||||
|
||||
function validateInputs() {
|
||||
// check if any new values are bad
|
||||
var tables = $('table');
|
||||
|
||||
var inputsValid = true;
|
||||
|
||||
var tables = $('table');
|
||||
|
||||
// clear any current invalid rows
|
||||
$('tr.' + Settings.INVALID_ROW_CLASS).removeClass(Settings.INVALID_ROW_CLASS);
|
||||
|
||||
function markParentRowInvalid(rowChild) {
|
||||
$(rowChild).closest('tr').addClass(Settings.INVALID_ROW_CLASS);
|
||||
}
|
||||
|
||||
_.each(tables, function(table) {
|
||||
var inputs = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ':not([data-category]) input[data-changed="true"]');
|
||||
|
||||
var empty = false;
|
||||
|
||||
_.each(inputs, function(input){
|
||||
var inputVal = $(input).val();
|
||||
|
||||
if (inputVal.length === 0) {
|
||||
empty = true
|
||||
|
||||
markParentRowInvalid(input);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (empty) {
|
||||
showErrorMessage("Error", "Empty field(s)");
|
||||
inputsValid = false;
|
||||
return
|
||||
}
|
||||
|
||||
// validate keys specificially for spaces and equality to an existing key
|
||||
var newKeys = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ' td.key');
|
||||
|
||||
var keyWithSpaces = false;
|
||||
var duplicateKey = false;
|
||||
|
||||
_.each(newKeys, function(keyCell) {
|
||||
var keyVal = $(keyCell).children('input').val();
|
||||
|
||||
if (keyVal.indexOf(' ') !== -1) {
|
||||
keyWithSpaces = true;
|
||||
markParentRowInvalid(keyCell);
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure we don't have duplicate keys in the table
|
||||
var otherKeys = $(table).find('td.key').not(keyCell);
|
||||
_.each(otherKeys, function(otherKeyCell) {
|
||||
var keyInput = $(otherKeyCell).children('input');
|
||||
|
||||
if (keyInput.length) {
|
||||
if ($(keyInput).val() == keyVal) {
|
||||
duplicateKey = true;
|
||||
}
|
||||
} else if ($(otherKeyCell).html() == keyVal) {
|
||||
duplicateKey = true;
|
||||
}
|
||||
|
||||
if (duplicateKey) {
|
||||
markParentRowInvalid(keyCell);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
if (keyWithSpaces) {
|
||||
showErrorMessage("Error", "Key contains spaces");
|
||||
inputsValid = false;
|
||||
return
|
||||
}
|
||||
|
||||
if (duplicateKey) {
|
||||
showErrorMessage("Error", "Two keys cannot be identical");
|
||||
inputsValid = false;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
return inputsValid;
|
||||
}
|
||||
|
||||
var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!";
|
||||
|
||||
function saveSettings() {
|
||||
// disable any inputs not changed
|
||||
$("input:not([data-changed])").each(function(){
|
||||
$(this).prop('disabled', true);
|
||||
});
|
||||
|
||||
// grab a JSON representation of the form via form2js
|
||||
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
|
||||
if (validateInputs()) {
|
||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||
|
||||
// check if we've set the basic http password - if so convert it to base64
|
||||
var canPost = true;
|
||||
if (formJSON["security"]) {
|
||||
var password = formJSON["security"]["http_password"];
|
||||
var verify_password = formJSON["security"]["verify_http_password"];
|
||||
if (password && password.length > 0) {
|
||||
if (password != verify_password) {
|
||||
bootbox.alert({"message": "Passwords must match!", "title":"Password Error"});
|
||||
canPost = false;
|
||||
} else {
|
||||
// disable any inputs not changed
|
||||
$("input:not([data-changed])").each(function(){
|
||||
$(this).prop('disabled', true);
|
||||
});
|
||||
|
||||
// grab a JSON representation of the form via form2js
|
||||
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
|
||||
|
||||
// check if we've set the basic http password - if so convert it to base64
|
||||
if (formJSON["security"]) {
|
||||
var password = formJSON["security"]["http_password"];
|
||||
if (password && password.length > 0) {
|
||||
formJSON["security"]["http_password"] = sha256_digest(password);
|
||||
delete formJSON["security"]["verify_http_password"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("----- SAVING ------");
|
||||
console.log(formJSON);
|
||||
// verify that the password and confirmation match before saving
|
||||
var canPost = true;
|
||||
|
||||
// re-enable all inputs
|
||||
$("input").each(function(){
|
||||
$(this).prop('disabled', false);
|
||||
});
|
||||
if (formJSON["security"]) {
|
||||
var password = formJSON["security"]["http_password"];
|
||||
var verify_password = formJSON["security"]["verify_http_password"];
|
||||
|
||||
// remove focus from the button
|
||||
$(this).blur();
|
||||
if (password && password.length > 0) {
|
||||
if (password != verify_password) {
|
||||
bootbox.alert({"message": "Passwords must match!", "title":"Password Error"});
|
||||
canPost = false;
|
||||
} else {
|
||||
formJSON["security"]["http_password"] = sha256_digest(password);
|
||||
delete formJSON["security"]["verify_http_password"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||
if (canPost) {
|
||||
postSettings(formJSON);
|
||||
console.log("----- SAVING ------");
|
||||
console.log(formJSON);
|
||||
|
||||
// re-enable all inputs
|
||||
$("input").each(function(){
|
||||
$(this).prop('disabled', false);
|
||||
});
|
||||
|
||||
// remove focus from the button
|
||||
$(this).blur();
|
||||
|
||||
if (canPost) {
|
||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||
postSettings(formJSON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1110,8 +1214,9 @@ function makeTable(setting, keypath, setting_value) {
|
|||
if (setting.can_add_new_categories) {
|
||||
html += makeTableCategoryInput(setting, numVisibleColumns);
|
||||
}
|
||||
|
||||
if (setting.can_add_new_rows || setting.can_add_new_categories) {
|
||||
html += makeTableInputs(setting, {}, "");
|
||||
html += makeTableHiddenInputs(setting, {}, "");
|
||||
}
|
||||
}
|
||||
html += "</table>"
|
||||
|
@ -1137,7 +1242,7 @@ function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns,
|
|||
return html;
|
||||
}
|
||||
|
||||
function makeTableInputs(setting, initialValues, categoryValue) {
|
||||
function makeTableHiddenInputs(setting, initialValues, categoryValue) {
|
||||
var html = "<tr class='inputs'" + (setting.can_add_new_categories && !categoryValue ? " hidden" : "") + " " +
|
||||
(categoryValue ? ("data-category='" + categoryValue + "'") : "") + " " +
|
||||
(setting.categorize_by_key ? ("data-keep-field='" + setting.categorize_by_key + "'") : "") + ">";
|
||||
|
@ -1148,7 +1253,7 @@ function makeTableInputs(setting, initialValues, categoryValue) {
|
|||
|
||||
if (setting.key) {
|
||||
html += "<td class='key' name='" + setting.key.name + "'>\
|
||||
<input type='text' class='form-control' placeholder='" + (_.has(setting.key, 'placeholder') ? setting.key.placeholder : "") + "' value=''>\
|
||||
<input type='text' style='display: none;' class='form-control' placeholder='" + (_.has(setting.key, 'placeholder') ? setting.key.placeholder : "") + "' value=''>\
|
||||
</td>"
|
||||
}
|
||||
|
||||
|
@ -1157,14 +1262,14 @@ function makeTableInputs(setting, initialValues, categoryValue) {
|
|||
if (col.type === "checkbox") {
|
||||
html +=
|
||||
"<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>" +
|
||||
"<input type='checkbox' class='form-control table-checkbox' " +
|
||||
"<input type='checkbox' style='display: none;' class='form-control table-checkbox' " +
|
||||
"name='" + col.name + "'" + (defaultValue ? " checked" : "") + "/>" +
|
||||
"</td>";
|
||||
} else {
|
||||
html +=
|
||||
"<td " + (col.hidden ? "style='display: none;'" : "") + " class='" + Settings.DATA_COL_CLASS + "' " +
|
||||
"name='" + col.name + "'>" +
|
||||
"<input type='text' class='form-control' placeholder='" + (col.placeholder ? col.placeholder : "") + "' " +
|
||||
"<input type='text' style='display: none;' class='form-control' placeholder='" + (col.placeholder ? col.placeholder : "") + "' " +
|
||||
"value='" + (defaultValue || "") + "' data-default='" + (defaultValue || "") + "'" +
|
||||
(col.readonly ? " readonly" : "") + ">" +
|
||||
"</td>";
|
||||
|
@ -1244,49 +1349,17 @@ function addTableRow(row) {
|
|||
|
||||
var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS);
|
||||
|
||||
var input_clone = row.clone();
|
||||
|
||||
if (!isArray) {
|
||||
// Check key spaces
|
||||
var key = row.children(".key").children("input").val()
|
||||
if (key.indexOf(' ') !== -1) {
|
||||
showErrorMessage("Error", "Key contains spaces")
|
||||
return
|
||||
}
|
||||
// Check keys with the same name
|
||||
var equals = false;
|
||||
_.each(columns.children(".key"), function(element) {
|
||||
if ($(element).text() === key) {
|
||||
equals = true
|
||||
return
|
||||
}
|
||||
})
|
||||
if (equals) {
|
||||
showErrorMessage("Error", "Two keys cannot be identical")
|
||||
return
|
||||
}
|
||||
// show the key input
|
||||
var keyInput = row.children(".key").children("input");
|
||||
}
|
||||
|
||||
// Check empty fields
|
||||
var empty = false;
|
||||
_.each(row.children('.' + Settings.DATA_COL_CLASS + ' input'), function(element) {
|
||||
if ($(element).val().length === 0) {
|
||||
empty = true
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if (empty) {
|
||||
showErrorMessage("Error", "Empty field(s)")
|
||||
return
|
||||
}
|
||||
|
||||
var input_clone = row.clone()
|
||||
|
||||
// Change input row to data row
|
||||
var table = row.parents("table")
|
||||
var setting_name = table.attr("name")
|
||||
var full_name = setting_name + "." + key
|
||||
row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS)
|
||||
row.removeClass("inputs")
|
||||
var table = row.parents("table");
|
||||
var setting_name = table.attr("name");
|
||||
row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS);
|
||||
|
||||
_.each(row.children(), function(element) {
|
||||
if ($(element).hasClass("numbered")) {
|
||||
|
@ -1308,56 +1381,43 @@ function addTableRow(row) {
|
|||
anchor.addClass(Settings.DEL_ROW_SPAN_CLASSES)
|
||||
} else if ($(element).hasClass("key")) {
|
||||
var input = $(element).children("input")
|
||||
$(element).html(input.val())
|
||||
input.remove()
|
||||
input.show();
|
||||
} else if ($(element).hasClass(Settings.DATA_COL_CLASS)) {
|
||||
// Hide inputs
|
||||
var input = $(element).find("input")
|
||||
var isCheckbox = false;
|
||||
var isTime = false;
|
||||
if (input.hasClass("table-checkbox")) {
|
||||
input = $(input).parent();
|
||||
isCheckbox = true;
|
||||
} else if (input.hasClass("table-time")) {
|
||||
input = $(input).parent();
|
||||
isTime = true;
|
||||
}
|
||||
// show inputs
|
||||
var input = $(element).find("input");
|
||||
input.show();
|
||||
|
||||
var val = input.val();
|
||||
if (isCheckbox) {
|
||||
// don't hide the checkbox
|
||||
val = $(input).find("input").is(':checked');
|
||||
} else if (isTime) {
|
||||
// don't hide the time
|
||||
} else {
|
||||
input.attr("type", "hidden")
|
||||
}
|
||||
var isCheckbox = input.hasClass("table-checkbox");
|
||||
|
||||
if (isArray) {
|
||||
var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length
|
||||
var key = $(element).attr('name')
|
||||
var key = $(element).attr('name');
|
||||
|
||||
// are there multiple columns or just one?
|
||||
// with multiple we have an array of Objects, with one we have an array of whatever the value type is
|
||||
var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length
|
||||
|
||||
if (isCheckbox) {
|
||||
$(input).find("input").attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
|
||||
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
|
||||
} else {
|
||||
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
|
||||
}
|
||||
} else {
|
||||
input.attr("name", full_name + "." + $(element).attr("name"))
|
||||
// because the name of the setting in question requires the key
|
||||
// setup a hook to change the HTML name of the element whenever the key changes
|
||||
var colName = $(element).attr("name");
|
||||
keyInput.on('change', function(){
|
||||
input.attr("name", setting_name + "." + $(this).val() + "." + colName);
|
||||
});
|
||||
}
|
||||
|
||||
if (isCheckbox) {
|
||||
$(input).find("input").attr("data-changed", "true");
|
||||
} else {
|
||||
input.attr("data-changed", "true");
|
||||
$(element).append(val);
|
||||
}
|
||||
} else {
|
||||
console.log("Unknown table element")
|
||||
console.log("Unknown table element");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1387,7 +1447,12 @@ function deleteTableRow($row) {
|
|||
$row.empty();
|
||||
|
||||
if (!isArray) {
|
||||
$row.html("<input type='hidden' class='form-control' name='" + $row.attr('name') + "' data-changed='true' value=''>");
|
||||
if ($row.attr('name')) {
|
||||
$row.html("<input type='hidden' class='form-control' name='" + $row.attr('name') + "' data-changed='true' value=''>");
|
||||
} else {
|
||||
// for rows that didn't have a key, simply remove the row
|
||||
$row.remove();
|
||||
}
|
||||
} else {
|
||||
if ($table.find('.' + Settings.DATA_ROW_CLASS + "[data-category='" + categoryName + "']").length <= 1) {
|
||||
// This is the last row of the category, so delete the header
|
||||
|
|
|
@ -33,6 +33,7 @@ Item {
|
|||
propagateComposedEvents: true
|
||||
acceptedButtons: "AllButtons"
|
||||
onClicked: {
|
||||
menu.visible = false;
|
||||
menu.done();
|
||||
mouse.accepted = false;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ Item {
|
|||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
menu.visible = false;
|
||||
root.triggered();
|
||||
menu.done();
|
||||
}
|
||||
|
|
|
@ -75,6 +75,14 @@ Item {
|
|||
source: buttonOutline
|
||||
}
|
||||
|
||||
function urlHelper(src) {
|
||||
if (src.match(/\bhttp/)) {
|
||||
return src;
|
||||
} else {
|
||||
return "../../../" + src;
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
width: 50
|
||||
|
@ -84,7 +92,7 @@ Item {
|
|||
anchors.bottomMargin: 5
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
fillMode: Image.Stretch
|
||||
source: "../../../" + tabletButton.icon
|
||||
source: tabletButton.urlHelper(tabletButton.icon)
|
||||
}
|
||||
|
||||
ColorOverlay {
|
||||
|
@ -185,7 +193,7 @@ Item {
|
|||
|
||||
PropertyChanges {
|
||||
target: icon
|
||||
source: "../../../" + tabletButton.activeIcon
|
||||
source: tabletButton.urlHelper(tabletButton.activeIcon)
|
||||
}
|
||||
},
|
||||
State {
|
||||
|
|
|
@ -688,6 +688,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
somethingChanged = true;
|
||||
_simulationOwner.clearCurrentOwner();
|
||||
}
|
||||
} else if (newSimOwner.matchesValidID(myNodeID) && !_hasBidOnSimulation) {
|
||||
// entity-server tells us that we have simulation ownership while we never requested this for this EntityItem,
|
||||
// this could happen when the user reloads the cache and entity tree.
|
||||
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
||||
somethingChanged = true;
|
||||
_simulationOwner.clearCurrentOwner();
|
||||
weOwnSimulation = false;
|
||||
} else if (_simulationOwner.set(newSimOwner)) {
|
||||
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
||||
somethingChanged = true;
|
||||
|
@ -1278,7 +1285,7 @@ void EntityItem::grabSimulationOwnership() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) {
|
||||
// we already own it
|
||||
_simulationOwner.promotePriority(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
_simulationOwner.promotePriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
} else {
|
||||
// we don't own it yet
|
||||
_simulationOwner.setPendingPriority(SCRIPT_GRAB_SIMULATION_PRIORITY, usecTimestampNow());
|
||||
|
@ -1889,6 +1896,10 @@ void EntityItem::setPendingOwnershipPriority(quint8 priority, const quint64& tim
|
|||
_simulationOwner.setPendingPriority(priority, timestamp);
|
||||
}
|
||||
|
||||
void EntityItem::rememberHasSimulationOwnershipBid() const {
|
||||
_hasBidOnSimulation = true;
|
||||
}
|
||||
|
||||
QString EntityItem::actionsToDebugString() {
|
||||
QString result;
|
||||
QVector<QByteArray> serializedActions;
|
||||
|
|
|
@ -321,6 +321,7 @@ public:
|
|||
void updateSimulationOwner(const SimulationOwner& owner);
|
||||
void clearSimulationOwnership();
|
||||
void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp);
|
||||
void rememberHasSimulationOwnershipBid() const;
|
||||
|
||||
const QString& getMarketplaceID() const { return _marketplaceID; }
|
||||
void setMarketplaceID(const QString& value) { _marketplaceID = value; }
|
||||
|
@ -497,16 +498,16 @@ protected:
|
|||
mutable AABox _cachedAABox;
|
||||
mutable AACube _maxAACube;
|
||||
mutable AACube _minAACube;
|
||||
mutable bool _recalcAABox = true;
|
||||
mutable bool _recalcMinAACube = true;
|
||||
mutable bool _recalcMaxAACube = true;
|
||||
mutable bool _recalcAABox { true };
|
||||
mutable bool _recalcMinAACube { true };
|
||||
mutable bool _recalcMaxAACube { true };
|
||||
|
||||
float _localRenderAlpha;
|
||||
float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg/m^3
|
||||
float _density { ENTITY_ITEM_DEFAULT_DENSITY }; // kg/m^3
|
||||
// NOTE: _volumeMultiplier is used to allow some mass properties code exist in the EntityItem base class
|
||||
// rather than in all of the derived classes. If we ever collapse these classes to one we could do it a
|
||||
// different way.
|
||||
float _volumeMultiplier = 1.0f;
|
||||
float _volumeMultiplier { 1.0f };
|
||||
glm::vec3 _gravity;
|
||||
glm::vec3 _acceleration;
|
||||
float _damping;
|
||||
|
@ -516,7 +517,7 @@ protected:
|
|||
|
||||
QString _script; /// the value of the script property
|
||||
QString _loadedScript; /// the value of _script when the last preload signal was sent
|
||||
quint64 _scriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP }; /// the script loaded property used for forced reload
|
||||
quint64 _scriptTimestamp { ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP }; /// the script loaded property used for forced reload
|
||||
|
||||
QString _serverScripts;
|
||||
/// keep track of time when _serverScripts property was last changed
|
||||
|
@ -524,7 +525,7 @@ protected:
|
|||
|
||||
/// the value of _scriptTimestamp when the last preload signal was sent
|
||||
// NOTE: on construction we want this to be different from _scriptTimestamp so we intentionally bump it
|
||||
quint64 _loadedScriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 };
|
||||
quint64 _loadedScriptTimestamp { ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 };
|
||||
|
||||
QString _collisionSoundURL;
|
||||
SharedSoundPointer _collisionSound;
|
||||
|
@ -562,8 +563,8 @@ protected:
|
|||
uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
|
||||
|
||||
// these backpointers are only ever set/cleared by friends:
|
||||
EntityTreeElementPointer _element = nullptr; // set by EntityTreeElement
|
||||
void* _physicsInfo = nullptr; // set by EntitySimulation
|
||||
EntityTreeElementPointer _element { nullptr }; // set by EntityTreeElement
|
||||
void* _physicsInfo { nullptr }; // set by EntitySimulation
|
||||
bool _simulated; // set by EntitySimulation
|
||||
|
||||
bool addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action);
|
||||
|
@ -580,12 +581,15 @@ protected:
|
|||
// are used to keep track of and work around this situation.
|
||||
void checkWaitingToRemove(EntitySimulationPointer simulation = nullptr);
|
||||
mutable QSet<QUuid> _actionsToRemove;
|
||||
mutable bool _actionDataDirty = false;
|
||||
mutable bool _actionDataNeedsTransmit = false;
|
||||
mutable bool _actionDataDirty { false };
|
||||
mutable bool _actionDataNeedsTransmit { false };
|
||||
// _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag
|
||||
static quint64 _rememberDeletedActionTime;
|
||||
mutable QHash<QUuid, quint64> _previouslyDeletedActions;
|
||||
|
||||
// per entity keep state if it ever bid on simulation, so that we can ignore false simulation ownership
|
||||
mutable bool _hasBidOnSimulation { false };
|
||||
|
||||
QUuid _sourceUUID; /// the server node UUID we came from
|
||||
|
||||
bool _clientOnly { false };
|
||||
|
@ -594,7 +598,7 @@ protected:
|
|||
// physics related changes from the network to suppress any duplicates and make
|
||||
// sure redundant applications are idempotent
|
||||
glm::vec3 _lastUpdatedPositionValue;
|
||||
glm::quat _lastUpdatedRotationValue;
|
||||
glm::quat _lastUpdatedRotationValue;
|
||||
glm::vec3 _lastUpdatedVelocityValue;
|
||||
glm::vec3 _lastUpdatedAngularVelocityValue;
|
||||
glm::vec3 _lastUpdatedAccelerationValue;
|
||||
|
|
|
@ -231,6 +231,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
// and make note of it now, so we can act on it right away.
|
||||
propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->rememberHasSimulationOwnershipBid();
|
||||
}
|
||||
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
|
@ -444,6 +445,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
// we make a bid for simulation ownership
|
||||
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->pokeSimulationOwnership();
|
||||
entity->rememberHasSimulationOwnershipBid();
|
||||
}
|
||||
}
|
||||
if (properties.parentRelatedPropertyChanged() && entity->computePuffedQueryAACube()) {
|
||||
|
|
|
@ -64,7 +64,7 @@ EntityTree::~EntityTree() {
|
|||
}
|
||||
|
||||
void EntityTree::setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist) {
|
||||
_entityScriptSourceWhitelist = entityScriptSourceWhitelist.split(',');
|
||||
_entityScriptSourceWhitelist = entityScriptSourceWhitelist.split(',', QString::SkipEmptyParts);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1111,7 +1111,15 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
endUpdate = usecTimestampNow();
|
||||
_totalUpdates++;
|
||||
} else if (message.getType() == PacketType::EntityAdd) {
|
||||
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
|
||||
bool failedAdd = !allowed;
|
||||
if (!allowed) {
|
||||
qCDebug(entities) << "Filtered entity add. ID:" << entityItemID;
|
||||
} else if (!senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
|
||||
failedAdd = true;
|
||||
qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID()
|
||||
<< "] attempted to add an entity ID:" << entityItemID;
|
||||
|
||||
} else {
|
||||
// this is a new entity... assign a new entityID
|
||||
properties.setCreated(properties.getLastEdited());
|
||||
properties.setLastEditedBy(senderNode->getUUID());
|
||||
|
@ -1126,7 +1134,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
startLogging = usecTimestampNow();
|
||||
if (wantEditLogging()) {
|
||||
qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:"
|
||||
<< newEntity->getEntityItemID();
|
||||
<< newEntity->getEntityItemID();
|
||||
qCDebug(entities) << " properties:" << properties;
|
||||
}
|
||||
if (wantTerseEditLogging()) {
|
||||
|
@ -1136,10 +1144,14 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
}
|
||||
endLogging = usecTimestampNow();
|
||||
|
||||
} else {
|
||||
failedAdd = true;
|
||||
qCDebug(entities) << "Add entity failed ID:" << entityItemID;
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID()
|
||||
<< "] attempted to add an entity.";
|
||||
}
|
||||
if (failedAdd) { // Let client know it failed, so that they don't have an entity that no one else sees.
|
||||
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
|
||||
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
|
||||
}
|
||||
} else {
|
||||
static QString repeatedMessage =
|
||||
|
|
|
@ -582,6 +582,8 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
_nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
// copy _outgoingPriority into pendingPriority...
|
||||
_entity->setPendingOwnershipPriority(_outgoingPriority, now);
|
||||
// don't forget to remember that we have made a bid
|
||||
_entity->rememberHasSimulationOwnershipBid();
|
||||
// ...then reset _outgoingPriority in preparation for the next frame
|
||||
_outgoingPriority = 0;
|
||||
} else if (_outgoingPriority != _entity->getSimulationPriority()) {
|
||||
|
|
|
@ -119,7 +119,7 @@ public:
|
|||
* @param msg {object|string}
|
||||
*/
|
||||
Q_INVOKABLE void emitScriptEvent(QVariant msg);
|
||||
|
||||
|
||||
Q_INVOKABLE bool onHomeScreen();
|
||||
|
||||
QObject* getTabletSurface();
|
||||
|
@ -170,14 +170,14 @@ public:
|
|||
/**jsdoc
|
||||
* Returns the current value of this button's properties
|
||||
* @function TabletButtonProxy#getProperties
|
||||
* @returns {object}
|
||||
* @returns {ButtonProperties}
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getProperties() const;
|
||||
|
||||
/**jsdoc
|
||||
* Replace the values of some of this button's properties
|
||||
* @function TabletButtonProxy#editProperties
|
||||
* @param properties {object} set of properties to change
|
||||
* @param {ButtonProperties} properties - set of properties to change
|
||||
*/
|
||||
Q_INVOKABLE void editProperties(QVariantMap properties);
|
||||
|
||||
|
@ -199,4 +199,13 @@ protected:
|
|||
QVariantMap _properties;
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* @typedef TabletButtonProxy.ButtonProperties
|
||||
* @property {string} text - button caption
|
||||
* @property {string} icon - url to button icon. (50 x 50)
|
||||
* @property {string} activeText - button caption when button is active
|
||||
* @property {string} activeIcon - url to button icon used when button is active. (50 x 50)
|
||||
* @property {string} isActive - true when button is active.
|
||||
*/
|
||||
|
||||
#endif // hifi_TabletScriptingInterface_h
|
||||
|
|
|
@ -24,6 +24,7 @@ var CAMERA_MATRIX = -7;
|
|||
var ROT_Y_180 = {x: 0, y: 1, z: 0, w: 0};
|
||||
var TABLET_TEXTURE_RESOLUTION = { x: 480, y: 706 };
|
||||
var INCHES_TO_METERS = 1 / 39.3701;
|
||||
var NO_HANDS = -1;
|
||||
|
||||
var TABLET_URL = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx";
|
||||
|
||||
|
@ -35,18 +36,21 @@ var TABLET_MODEL_PATH = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-wi
|
|||
// * position - position in front of the user
|
||||
// * rotation - rotation of entity so it faces the user.
|
||||
function calcSpawnInfo(hand, height) {
|
||||
var noHands = -1;
|
||||
var finalPosition;
|
||||
if (HMD.active && hand !== noHands) {
|
||||
|
||||
var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;
|
||||
var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation;
|
||||
|
||||
if (HMD.active && hand !== NO_HANDS) {
|
||||
var handController = getControllerWorldLocation(hand, true);
|
||||
var controllerPosition = handController.position;
|
||||
|
||||
// compute the angle of the chord with length (height / 2)
|
||||
var theta = Math.asin(height / (2 * Vec3.distance(HMD.position, controllerPosition)));
|
||||
var theta = Math.asin(height / (2 * Vec3.distance(headPos, controllerPosition)));
|
||||
|
||||
// then we can use this angle to rotate the vector between the HMD position and the center of the tablet.
|
||||
// this vector, u, will become our new look at direction.
|
||||
var d = Vec3.normalize(Vec3.subtract(HMD.position, controllerPosition));
|
||||
var d = Vec3.normalize(Vec3.subtract(headPos, controllerPosition));
|
||||
var w = Vec3.normalize(Vec3.cross(Y_AXIS, d));
|
||||
var q = Quat.angleAxis(theta * (180 / Math.PI), w);
|
||||
var u = Vec3.multiplyQbyV(q, d);
|
||||
|
@ -64,8 +68,8 @@ function calcSpawnInfo(hand, height) {
|
|||
rotation: lookAtRot
|
||||
};
|
||||
} else {
|
||||
var front = Quat.getFront(Camera.orientation);
|
||||
finalPosition = Vec3.sum(Camera.position, Vec3.multiply(0.6, front));
|
||||
var front = Quat.getFront(headRot);
|
||||
finalPosition = Vec3.sum(headPos, Vec3.multiply(0.6, front));
|
||||
var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, front, {x: 0, y: 1, z: 0});
|
||||
return {
|
||||
position: finalPosition,
|
||||
|
@ -198,6 +202,11 @@ WebTablet = function (url, width, dpi, hand, clientOnly) {
|
|||
_this.geometryChanged(geometry);
|
||||
};
|
||||
Window.geometryChanged.connect(this.myGeometryChanged);
|
||||
|
||||
this.myCameraModeChanged = function(newMode) {
|
||||
_this.cameraModeChanged(newMode);
|
||||
};
|
||||
Camera.modeUpdated.connect(this.myCameraModeChanged);
|
||||
};
|
||||
|
||||
WebTablet.prototype.setHomeButtonTexture = function() {
|
||||
|
@ -228,11 +237,11 @@ WebTablet.prototype.destroy = function () {
|
|||
Controller.mouseReleaseEvent.disconnect(this.myMouseReleaseEvent);
|
||||
|
||||
Window.geometryChanged.disconnect(this.myGeometryChanged);
|
||||
Camera.modeUpdated.disconnect(this.myCameraModeChanged);
|
||||
};
|
||||
|
||||
WebTablet.prototype.geometryChanged = function (geometry) {
|
||||
if (!HMD.active) {
|
||||
var NO_HANDS = -1;
|
||||
var tabletProperties = {};
|
||||
// compute position, rotation & parentJointIndex of the tablet
|
||||
this.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties);
|
||||
|
@ -288,7 +297,6 @@ WebTablet.prototype.onHmdChanged = function () {
|
|||
Controller.mouseReleaseEvent.connect(this.myMouseReleaseEvent);
|
||||
}
|
||||
|
||||
var NO_HANDS = -1;
|
||||
var tabletProperties = {};
|
||||
// compute position, rotation & parentJointIndex of the tablet
|
||||
this.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties);
|
||||
|
@ -370,6 +378,18 @@ WebTablet.prototype.mousePressEvent = function (event) {
|
|||
}
|
||||
};
|
||||
|
||||
WebTablet.prototype.cameraModeChanged = function (newMode) {
|
||||
// reposition the tablet.
|
||||
// This allows HMD.position to reflect the new camera mode.
|
||||
if (HMD.active) {
|
||||
var self = this;
|
||||
var tabletProperties = {};
|
||||
// compute position, rotation & parentJointIndex of the tablet
|
||||
self.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties);
|
||||
Entities.editEntity(self.tabletEntityID, tabletProperties);
|
||||
}
|
||||
};
|
||||
|
||||
function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) {
|
||||
var rayDirectionDotPlaneNormal = Vec3.dot(rayDirection, planeNormal);
|
||||
if (rayDirectionDotPlaneNormal > 0.00001 || rayDirectionDotPlaneNormal < -0.00001) {
|
||||
|
|
21
unpublishedScripts/marketplace/teaLight/teaLight.js
Normal file
21
unpublishedScripts/marketplace/teaLight/teaLight.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
(function() {
|
||||
var MINIMUM_LIGHT_INTENSITY = 100.0;
|
||||
var MAXIMUM_LIGHT_INTENSITY = 125.0;
|
||||
|
||||
// Return a random number between `low` (inclusive) and `high` (exclusive)
|
||||
function randFloat(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.preload = function(entityID) {
|
||||
self.intervalID = Script.setInterval(function() {
|
||||
Entities.editEntity(entityID, {
|
||||
intensity: randFloat(MINIMUM_LIGHT_INTENSITY, MAXIMUM_LIGHT_INTENSITY)
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
this.unload = function() {
|
||||
Script.clearInterval(self.intervalID);
|
||||
}
|
||||
});
|
Loading…
Reference in a new issue