Merge branch 'master' into feature/jwst-update

This commit is contained in:
Malin E
2022-04-08 17:24:16 +02:00
68 changed files with 3625 additions and 81699 deletions
-2
View File
@@ -82,7 +82,6 @@ set(OPENSPACE_SOURCE
${OPENSPACE_BASE_DIR}/src/network/parallelconnection.cpp
${OPENSPACE_BASE_DIR}/src/network/parallelpeer.cpp
${OPENSPACE_BASE_DIR}/src/network/parallelpeer_lua.inl
${OPENSPACE_BASE_DIR}/src/network/parallelserver.cpp
${OPENSPACE_BASE_DIR}/src/properties/optionproperty.cpp
${OPENSPACE_BASE_DIR}/src/properties/property.cpp
${OPENSPACE_BASE_DIR}/src/properties/propertyowner.cpp
@@ -262,7 +261,6 @@ set(OPENSPACE_HEADER
${OPENSPACE_BASE_DIR}/include/openspace/navigation/waypoint.h
${OPENSPACE_BASE_DIR}/include/openspace/network/parallelconnection.h
${OPENSPACE_BASE_DIR}/include/openspace/network/parallelpeer.h
${OPENSPACE_BASE_DIR}/include/openspace/network/parallelserver.h
${OPENSPACE_BASE_DIR}/include/openspace/network/messagestructures.h
${OPENSPACE_BASE_DIR}/include/openspace/network/messagestructureshelper.h
${OPENSPACE_BASE_DIR}/include/openspace/properties/listproperty.h
+7 -1
View File
@@ -1659,8 +1659,14 @@ void OpenSpaceEngine::removeModeChangeCallback(CallbackHandle handle) {
void setCameraFromProfile(const Profile& p) {
if (!p.camera.has_value()) {
throw ghoul::RuntimeError("No 'camera' entry exists in the startup profile");
// If the camera is not specified, we want to set it to a sensible default value
interaction::NavigationState nav;
nav.anchor = "Root";
nav.referenceFrame = "Root";
global::navigationHandler->setNavigationStateNextFrame(nav);
return;
}
std::visit(
overloaded{
[](const Profile::CameraNavState& navStateProfile) {
+106 -51
View File
@@ -40,6 +40,7 @@
#include <openspace/util/universalhelpers.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/interpolator.h>
#include <glm/ext/quaternion_relational.hpp>
namespace {
constexpr const char _loggerCat[] = "Path";
@@ -131,7 +132,6 @@ Path::Path(Waypoint start, Waypoint end, Type type,
// We now know how long it took to traverse the path. Use that
_speedFactorFromDuration = _progressedTime / *duration;
resetPlaybackVariables();
}
}
@@ -161,23 +161,7 @@ CameraPose Path::traversePath(double dt, float speedScale) {
if (_type == Type::Linear) {
// Special handling of linear paths, so that it can be used when we are
// traversing very large distances without introducing precision problems
const glm::dvec3 prevPosToEnd = _prevPose.position - _end.position();
const double remainingDistance = glm::length(prevPosToEnd);
// Actual displacement may not be bigger than remaining distance
if (displacement > remainingDistance) {
displacement = remainingDistance;
_traveledDistance = pathLength();
_shouldQuit = true;
return _end.pose();
}
// Just move along the line from the current position to the target
newPose.position = _prevPose.position -
displacement * glm::normalize(prevPosToEnd);
const double relativeDistance = _traveledDistance / pathLength();
newPose.rotation = interpolateRotation(relativeDistance);
newPose = linearInterpolatedPose(_traveledDistance, displacement);
}
else {
if (std::abs(prevDistance - _traveledDistance) < LengthEpsilon) {
@@ -204,7 +188,14 @@ bool Path::hasReachedEnd() const {
return true;
}
return (_traveledDistance / pathLength()) >= 1.0;
bool isPositionFinished = (_traveledDistance / pathLength()) >= 1.0;
bool isRotationFinished = glm::all(glm::equal(
_prevPose.rotation,
_end.rotation(),
glm::epsilon<double>()
));
return isPositionFinished && isRotationFinished;
}
void Path::resetPlaybackVariables() {
@@ -214,6 +205,28 @@ void Path::resetPlaybackVariables() {
_shouldQuit = false;
}
CameraPose Path::linearInterpolatedPose(double distance, double displacement) {
ghoul_assert(_type == Type::Linear, "Path type must be linear");
const double relativeDistance = distance / pathLength();
const glm::dvec3 prevPosToEnd = _prevPose.position - _end.position();
const double remainingDistance = glm::length(prevPosToEnd);
CameraPose pose;
// Actual displacement may not be bigger than remaining distance
if (displacement > remainingDistance) {
_traveledDistance = pathLength();
pose.position = _end.position();
}
else {
// Just move along line from the current position to the target
const glm::dvec3 lineDir = glm::normalize(prevPosToEnd);
pose.position = _prevPose.position - displacement * lineDir;
}
pose.rotation = linearPathRotation(relativeDistance);
return pose;
}
CameraPose Path::interpolatedPose(double distance) const {
const double relativeDistance = distance / pathLength();
CameraPose cs;
@@ -227,6 +240,8 @@ glm::dquat Path::interpolateRotation(double t) const {
case Type::AvoidCollision:
return easedSlerpRotation(t);
case Type::Linear:
// @TODO (2022-03-29, emmbr) Fix so that rendering the rotation of linear
// paths works again. I.e. openspace.debugging.renderCameraPath
return linearPathRotation(t);
case Type::ZoomOutOverview:
case Type::AvoidCollisionWithLookAt:
@@ -243,45 +258,59 @@ glm::dquat Path::easedSlerpRotation(double t) const {
}
glm::dquat Path::linearPathRotation(double t) const {
const double tHalf = 0.5;
const glm::dvec3 a = ghoul::viewDirection(_start.rotation());
const glm::dvec3 b = ghoul::viewDirection(_end.rotation());
const double angle = std::acos(glm::dot(a, b)); // assumes length 1.0 for a & b
const glm::dvec3 endNodePos = _end.node()->worldPosition();
const glm::dvec3 endUp = _end.rotation() * glm::dvec3(0.0, 1.0, 0.0);
// Seconds per pi angles. Per default, it takes 5 seconds to turn 90 degrees
double factor = 5.0 / glm::half_pi<double>();
factor *= global::navigationHandler->pathNavigator().linearRotationSpeedFactor();
if (t < tHalf) {
// Interpolate to look at target
const glm::dvec3 halfWayPosition = _curve->positionAt(tHalf);
const glm::dquat q = ghoul::lookAtQuaternion(halfWayPosition, endNodePos, endUp);
double turnDuration = std::max(angle * factor, 1.0); // Always at least 1 second
const double time = glm::clamp(_progressedTime / turnDuration, 0.0, 1.0);
return easedSlerpRotation(time);
const double tScaled = ghoul::sineEaseInOut(t / tHalf);
return glm::slerp(_start.rotation(), q, tScaled);
}
// @TODO (2022-03-18, emmbr) Leaving this for now, as something similar might have to
// be implemented for navigation states. But should be removed/reimplemented
// This distance is guaranteed to be strictly decreasing for linear paths
const double distanceToEnd = glm::distance(_prevPose.position, _end.position());
//const glm::dvec3 endNodePos = _end.node()->worldPosition();
//const glm::dvec3 endUp = _end.rotation() * glm::dvec3(0.0, 1.0, 0.0);
// Determine the distance at which to start interpolating to the target rotation.
// The magic numbers here are just randomly picked constants, set to make the
// resulting rotation look ok-ish
double closingUpDistance = 10.0 * _end.validBoundingSphere();
if (pathLength() < 2.0 * closingUpDistance) {
closingUpDistance = 0.2 * pathLength();
}
//const double tHalf = 0.5;
//if (t < tHalf) {
// // Interpolate to look at target
// const glm::dvec3 halfWayPosition = _curve->positionAt(tHalf);
// const glm::dquat q = ghoul::lookAtQuaternion(halfWayPosition, endNodePos, endUp);
if (distanceToEnd < closingUpDistance) {
// Interpolate to target rotation
const double tScaled = ghoul::sineEaseInOut(1.0 - distanceToEnd / closingUpDistance);
// const double tScaled = ghoul::sineEaseInOut(t / tHalf);
// return glm::slerp(_start.rotation(), q, tScaled);
//}
// Compute a position in front of the camera at the end orientation
const double inFrontDistance = glm::distance(_end.position(), endNodePos);
const glm::dvec3 viewDir = ghoul::viewDirection(_end.rotation());
const glm::dvec3 inFrontOfEnd = _end.position() + inFrontDistance * viewDir;
const glm::dvec3 lookAtPos = ghoul::interpolateLinear(tScaled, endNodePos, inFrontOfEnd);
return ghoul::lookAtQuaternion(_prevPose.position, lookAtPos, endUp);
}
//// This distance is guaranteed to be strictly decreasing for linear paths
//const double distanceToEnd = glm::distance(_prevPose.position, _end.position());
// Keep looking at the end node
return ghoul::lookAtQuaternion(_prevPose.position, endNodePos, endUp);
//// Determine the distance at which to start interpolating to the target rotation.
//// The magic numbers here are just randomly picked constants, set to make the
//// resulting rotation look ok-ish
//double closingUpDistance = 10.0 * _end.validBoundingSphere();
//if (pathLength() < 2.0 * closingUpDistance) {
// closingUpDistance = 0.2 * pathLength();
//}
//if (distanceToEnd < closingUpDistance) {
// // Interpolate to target rotation
// const double tScaled = ghoul::sineEaseInOut(1.0 - distanceToEnd / closingUpDistance);
// // Compute a position in front of the camera at the end orientation
// const double inFrontDistance = glm::distance(_end.position(), endNodePos);
// const glm::dvec3 viewDir = ghoul::viewDirection(_end.rotation());
// const glm::dvec3 inFrontOfEnd = _end.position() + inFrontDistance * viewDir;
// const glm::dvec3 lookAtPos = ghoul::interpolateLinear(tScaled, endNodePos, inFrontOfEnd);
// return ghoul::lookAtQuaternion(_prevPose.position, lookAtPos, endUp);
//}
//// Keep looking at the end node
//return ghoul::lookAtQuaternion(_prevPose.position, endNodePos, endUp);
}
glm::dquat Path::lookAtTargetsRotation(double t) const {
@@ -350,8 +379,17 @@ double Path::speedAlongPath(double traveledDistance) const {
double startUpDistance = DampenDistanceFactor * _start.validBoundingSphere();
double closeUpDistance = DampenDistanceFactor * _end.validBoundingSphere();
// Kind of an ugly workaround to make damping behave over very long paths, and/or
// where the target nodes might have large bounding spheres. The current max is set
// based on the order of magnitude of the solar system, which ofc is very specific to
// our space content...
// @TODO (2022-03-22, emmbr) Come up with a better more general solution
constexpr const double MaxDistance = 1E12;
startUpDistance = glm::min(MaxDistance, startUpDistance);
closeUpDistance = glm::min(MaxDistance, closeUpDistance);
if (pathLength() < startUpDistance + closeUpDistance) {
startUpDistance = 0.49 * pathLength(); // a little less than half
startUpDistance = 0.4 * pathLength(); // a little less than half
closeUpDistance = startUpDistance;
}
@@ -619,6 +657,23 @@ Path createPathFromDictionary(const ghoul::Dictionary& dictionary,
p.useTargetUpDirection.value_or(false)
};
double startToTargetCenterDistance = glm::distance(
startPoint.position(),
targetNode->worldPosition()
);
// Use a linear path if camera start is within the bounding sphere
const PathNavigator& navigator = global::navigationHandler->pathNavigator();
const double bs = navigator.findValidBoundingSphere(targetNode);
bool withinTargetBoundingSphere = startToTargetCenterDistance < bs;
if (withinTargetBoundingSphere) {
LINFO(
"Camera is within the bounding sphere of the target node. "
"Using linear path"
);
type = Path::Type::Linear;
}
waypoints = { computeWaypointFromNodeInfo(info, startPoint, type) };
break;
}
+15 -1
View File
@@ -69,7 +69,7 @@ namespace {
constexpr openspace::properties::Property::PropertyInfo SpeedScaleInfo = {
"SpeedScale",
"Speed Scale",
"Scale factor that the speed will be mulitplied with during path traversal. "
"Scale factor that the speed will be multiplied with during path traversal. "
"Can be used to speed up or slow down the camera motion, depending on if the "
"value is larger than or smaller than one."
};
@@ -90,6 +90,14 @@ namespace {
"object."
};
constexpr openspace::properties::Property::PropertyInfo RotationSpeedFactorInfo = {
"RotationSpeedFactor",
"Rotation Speed Factor (Linear Path)",
"Affects how fast the camera rotates to the target rotation during a linear "
"path. A value of 1 means that the camera will rotate 90 degrees in about 5 "
"seconds. A value of 2 means twice that fast, and so on."
};
constexpr const openspace::properties::Property::PropertyInfo MinBoundingSphereInfo =
{
"MinimalValidBoundingSphere",
@@ -119,6 +127,7 @@ PathNavigator::PathNavigator()
, _speedScale(SpeedScaleInfo, 1.f, 0.01f, 2.f)
, _applyIdleBehaviorOnFinish(IdleBehaviorOnFinishInfo, false)
, _arrivalDistanceFactor(ArrivalDistanceFactorInfo, 2.0, 0.1, 20.0)
, _linearRotationSpeedFactor(RotationSpeedFactorInfo, 1.f, 0.1f, 2.f)
, _minValidBoundingSphere(MinBoundingSphereInfo, 10.0, 1.0, 3e10)
, _relevantNodeTags(RelevantNodeTagsInfo)
{
@@ -134,6 +143,7 @@ PathNavigator::PathNavigator()
addProperty(_speedScale);
addProperty(_applyIdleBehaviorOnFinish);
addProperty(_arrivalDistanceFactor);
addProperty(_linearRotationSpeedFactor);
addProperty(_minValidBoundingSphere);
_relevantNodeTags = std::vector<std::string>{
@@ -166,6 +176,10 @@ double PathNavigator::arrivalDistanceFactor() const {
return _arrivalDistanceFactor;
}
float PathNavigator::linearRotationSpeedFactor() const {
return _linearRotationSpeedFactor;
}
bool PathNavigator::hasCurrentPath() const {
return _currentPath != nullptr;
}
+30 -18
View File
@@ -37,7 +37,8 @@ namespace {
namespace openspace {
const unsigned int ParallelConnection::ProtocolVersion = 5;
// Gonna do some UTF-like magic once we reach 255 to introduce a second byte or something
const uint8_t ParallelConnection::ProtocolVersion = 6;
ParallelConnection::Message::Message(MessageType t, std::vector<char> c)
: type(t)
@@ -52,8 +53,9 @@ ParallelConnection::DataMessage::DataMessage(datamessagestructures::Type t,
, content(std::move(c))
{}
ParallelConnection::ConnectionLostError::ConnectionLostError()
ParallelConnection::ConnectionLostError::ConnectionLostError(bool shouldLogError_)
: ghoul::RuntimeError("Parallel connection lost", "ParallelConnection")
, shouldLogError(shouldLogError_)
{}
ParallelConnection::ParallelConnection(std::unique_ptr<ghoul::io::TcpSocket> socket)
@@ -65,14 +67,14 @@ bool ParallelConnection::isConnectedOrConnecting() const {
}
void ParallelConnection::sendDataMessage(const DataMessage& dataMessage) {
const uint32_t dataMessageTypeOut = static_cast<uint32_t>(dataMessage.type);
const uint8_t dataMessageTypeOut = static_cast<uint8_t>(dataMessage.type);
const double dataMessageTimestamp = dataMessage.timestamp;
std::vector<char> messageContent;
messageContent.insert(
messageContent.end(),
reinterpret_cast<const char*>(&dataMessageTypeOut),
reinterpret_cast<const char*>(&dataMessageTypeOut) + sizeof(uint32_t)
reinterpret_cast<const char*>(&dataMessageTypeOut) + sizeof(uint8_t)
);
messageContent.insert(
@@ -90,7 +92,7 @@ void ParallelConnection::sendDataMessage(const DataMessage& dataMessage) {
}
bool ParallelConnection::sendMessage(const Message& message) {
const uint32_t messageTypeOut = static_cast<uint32_t>(message.type);
const uint8_t messageTypeOut = static_cast<uint8_t>(message.type);
const uint32_t messageSizeOut = static_cast<uint32_t>(message.content.size());
std::vector<char> header;
@@ -100,12 +102,12 @@ bool ParallelConnection::sendMessage(const Message& message) {
header.insert(header.end(),
reinterpret_cast<const char*>(&ProtocolVersion),
reinterpret_cast<const char*>(&ProtocolVersion) + sizeof(uint32_t)
reinterpret_cast<const char*>(&ProtocolVersion) + sizeof(uint8_t)
);
header.insert(header.end(),
reinterpret_cast<const char*>(&messageTypeOut),
reinterpret_cast<const char*>(&messageTypeOut) + sizeof(uint32_t)
reinterpret_cast<const char*>(&messageTypeOut) + sizeof(uint8_t)
);
header.insert(header.end(),
@@ -123,6 +125,7 @@ bool ParallelConnection::sendMessage(const Message& message) {
}
void ParallelConnection::disconnect() {
_shouldDisconnect = true;
if (_socket) {
_socket->disconnect();
}
@@ -136,7 +139,9 @@ ParallelConnection::Message ParallelConnection::receiveMessage() {
// Header consists of...
constexpr size_t HeaderSize =
2 * sizeof(char) + // OS
3 * sizeof(uint32_t); // Protocol version, message type and message size
sizeof(uint8_t) + // Protocol version
sizeof(uint8_t) + // Message type
sizeof(uint32_t); // message size
// Create basic buffer for receiving first part of messages
std::vector<char> headerBuffer(HeaderSize);
@@ -144,20 +149,27 @@ ParallelConnection::Message ParallelConnection::receiveMessage() {
// Receive the header data
if (!_socket->get(headerBuffer.data(), HeaderSize)) {
LERROR("Failed to read header from socket. Disconencting.");
throw ConnectionLostError();
// The `get` call is blocking until something happens, so we might end up here if
// the socket properly closed or if the loading legitimately failed
if (_shouldDisconnect) {
throw ConnectionLostError(false);
}
else {
LERROR("Failed to read header from socket. Disconnecting");
throw ConnectionLostError();
}
}
// Make sure that header matches this version of OpenSpace
if (!(headerBuffer[0] == 'O' && headerBuffer[1] == 'S')) {
LERROR("Expected to read message header 'OS' from socket.");
LERROR("Expected to read message header 'OS' from socket");
throw ConnectionLostError();
}
size_t offset = 2;
const uint32_t protocolVersionIn =
*reinterpret_cast<uint32_t*>(headerBuffer.data() + offset);
offset += sizeof(uint32_t);
const uint8_t protocolVersionIn =
*reinterpret_cast<uint8_t*>(headerBuffer.data() + offset);
offset += sizeof(uint8_t);
if (protocolVersionIn != ProtocolVersion) {
LERROR(fmt::format(
@@ -168,9 +180,9 @@ ParallelConnection::Message ParallelConnection::receiveMessage() {
throw ConnectionLostError();
}
const uint32_t messageTypeIn =
*reinterpret_cast<uint32_t*>(headerBuffer.data() + offset);
offset += sizeof(uint32_t);
const uint8_t messageTypeIn =
*reinterpret_cast<uint8_t*>(headerBuffer.data() + offset);
offset += sizeof(uint8_t);
const uint32_t messageSizeIn =
*reinterpret_cast<uint32_t*>(headerBuffer.data() + offset);
@@ -181,7 +193,7 @@ ParallelConnection::Message ParallelConnection::receiveMessage() {
// Receive the payload
messageBuffer.resize(messageSize);
if (!_socket->get(messageBuffer.data(), messageSize)) {
LERROR("Failed to read message from socket. Disconencting.");
LERROR("Failed to read message from socket. Disconnecting");
throw ConnectionLostError();
}
+57 -26
View File
@@ -163,31 +163,59 @@ void ParallelPeer::disconnect() {
}
void ParallelPeer::sendAuthentication() {
std::string name = _name;
// Length of this nodes name
const uint32_t nameLength = static_cast<uint32_t>(name.length());
std::string password = _password;
if (password.size() > std::numeric_limits<uint16_t>::max()) {
password.resize(std::numeric_limits<uint16_t>::max());
}
const uint16_t passwordSize = static_cast<uint16_t>(password.size());
// Total size of the buffer: (passcode + namelength + name)
const size_t size = sizeof(uint64_t) + sizeof(uint32_t) + nameLength;
std::string hostPassword = _hostPassword;
if (hostPassword.size() > std::numeric_limits<uint16_t>::max()) {
hostPassword.resize(std::numeric_limits<uint16_t>::max());
}
const uint16_t hostPasswordSize = static_cast<uint16_t>(hostPassword.size());
std::string name = _name;
if (name.size() > std::numeric_limits<uint8_t>::max()) {
name.resize(std::numeric_limits<uint8_t>::max());
}
const uint8_t nameLength = static_cast<uint8_t>(name.length());
// Total size of the buffer
const size_t size =
sizeof(uint16_t) + // password length
passwordSize + // password
sizeof(uint16_t) + // host password length
hostPasswordSize + // host password
sizeof(uint8_t) + // name length
nameLength; // name
// Create and reserve buffer
std::vector<char> buffer;
buffer.reserve(size);
const uint64_t passCode = std::hash<std::string>{}(_password.value());
// Write the hashed password to buffer
// Write the password to buffer
buffer.insert(
buffer.end(),
reinterpret_cast<const char*>(&passCode),
reinterpret_cast<const char*>(&passCode) + sizeof(uint64_t)
reinterpret_cast<const char*>(&passwordSize),
reinterpret_cast<const char*>(&passwordSize) + sizeof(uint16_t)
);
buffer.insert(buffer.end(), password.begin(), password.end());
// Write the host password to buffer
buffer.insert(
buffer.end(),
reinterpret_cast<const char*>(&hostPasswordSize),
reinterpret_cast<const char*>(&hostPasswordSize) + sizeof(uint16_t)
);
buffer.insert(buffer.end(), hostPassword.begin(), hostPassword.end());
// Write the length of the nodes name to buffer
buffer.insert(
buffer.end(),
reinterpret_cast<const char*>(&nameLength),
reinterpret_cast<const char*>(&nameLength) + sizeof(uint32_t)
reinterpret_cast<const char*>(&nameLength) + sizeof(uint8_t)
);
// Write this node's name to buffer
@@ -265,8 +293,8 @@ void ParallelPeer::dataMessageReceived(const std::vector<char>& message) {
size_t offset = 0;
// The type of data message received
const uint32_t type = *(reinterpret_cast<const uint32_t*>(message.data() + offset));
offset += sizeof(uint32_t);
const uint8_t type = *(reinterpret_cast<const uint8_t*>(message.data() + offset));
offset += sizeof(uint8_t);
const double timestamp = *(reinterpret_cast<const double*>(message.data() + offset));
offset += sizeof(double);
@@ -363,19 +391,19 @@ void ParallelPeer::dataMessageReceived(const std::vector<char>& message) {
}
void ParallelPeer::connectionStatusMessageReceived(const std::vector<char>& message) {
if (message.size() < 2 * sizeof(uint32_t)) {
if (message.size() < 2 * sizeof(uint8_t)) {
LERROR("Malformed connection status message");
return;
}
size_t pointer = 0;
uint32_t statusIn = *(reinterpret_cast<const uint32_t*>(&message[pointer]));
const uint8_t statusIn = *(reinterpret_cast<const uint8_t*>(&message[pointer]));
const ParallelConnection::Status status = static_cast<ParallelConnection::Status>(
statusIn
);
pointer += sizeof(uint32_t);
pointer += sizeof(uint8_t);
const size_t hostNameSize = *(reinterpret_cast<const uint32_t*>(&message[pointer]));
pointer += sizeof(uint32_t);
const uint8_t hostNameSize = *(reinterpret_cast<const uint8_t*>(&message[pointer]));
pointer += sizeof(uint8_t);
if (hostNameSize > message.size() - pointer) {
LERROR("Malformed connection status message");
@@ -409,8 +437,7 @@ void ParallelPeer::connectionStatusMessageReceived(const std::vector<char>& mess
global::timeManager->clearKeyframes();
}
void ParallelPeer::nConnectionsMessageReceived(const std::vector<char>& message)
{
void ParallelPeer::nConnectionsMessageReceived(const std::vector<char>& message) {
if (message.size() < sizeof(uint32_t)) {
LERROR("Malformed host info message");
return;
@@ -424,8 +451,10 @@ void ParallelPeer::handleCommunication() {
try {
ParallelConnection::Message m = _connection.receiveMessage();
queueInMessage(m);
} catch (const ParallelConnection::ConnectionLostError&) {
LERROR("Parallel connection lost");
} catch (const ParallelConnection::ConnectionLostError& e) {
if (e.shouldLogError) {
LERROR("Parallel connection lost");
}
}
}
setStatus(ParallelConnection::Status::Disconnected);
@@ -445,12 +474,14 @@ void ParallelPeer::setName(std::string name) {
void ParallelPeer::requestHostship() {
std::vector<char> buffer;
uint64_t passwordHash = std::hash<std::string>{}(_hostPassword);
std::string hostPw = _hostPassword;
uint16_t hostPwSize = static_cast<uint16_t>(hostPw.size());
buffer.insert(
buffer.end(),
reinterpret_cast<char*>(&passwordHash),
reinterpret_cast<char*>(&passwordHash) + sizeof(uint64_t)
reinterpret_cast<const char*>(&hostPwSize),
reinterpret_cast<const char*>(&hostPwSize) + sizeof(uint16_t)
);
buffer.insert(buffer.end(), hostPw.begin(), hostPw.end());
_connection.sendMessage(ParallelConnection::Message(
ParallelConnection::MessageType::HostshipRequest,
@@ -504,7 +535,7 @@ void ParallelPeer::resetTimeOffset() {
void ParallelPeer::preSynchronization() {
ZoneScoped
std::unique_lock<std::mutex> unqlock(_receiveBufferMutex);
std::unique_lock<std::mutex> unlock(_receiveBufferMutex);
while (!_receiveBuffer.empty()) {
ParallelConnection::Message& message = _receiveBuffer.front();
handleMessage(message);
-426
View File
@@ -1,426 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2022 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <openspace/network/parallelserver.h>
#include <ghoul/fmt.h>
#include <ghoul/io/socket/tcpsocket.h>
#include <ghoul/logging/logmanager.h>
#include <functional>
// @TODO(abock): In the entire class remove std::shared_ptr<Peer> by const Peer& where
// possible to simplify the interface
namespace {
constexpr const char* _loggerCat = "ParallelServer";
} // namespace
namespace openspace {
void ParallelServer::start(int port, const std::string& password,
const std::string& changeHostPassword)
{
_socketServer.listen(port);
_passwordHash = std::hash<std::string>{}(password);
_changeHostPasswordHash = std::hash<std::string>{}(changeHostPassword);
_serverThread = std::thread([this](){ handleNewPeers(); });
_eventLoopThread = std::thread([this]() { eventLoop(); });
}
void ParallelServer::setDefaultHostAddress(std::string defaultHostAddress) {
std::lock_guard lock(_hostInfoMutex);
_defaultHostAddress = std::move(defaultHostAddress);
}
std::string ParallelServer::defaultHostAddress() const {
std::lock_guard lock(_hostInfoMutex);
return _defaultHostAddress;
}
void ParallelServer::stop() {
_shouldStop = true;
_socketServer.close();
}
void ParallelServer::handleNewPeers() {
while (!_shouldStop) {
std::unique_ptr<ghoul::io::TcpSocket> s = _socketServer.awaitPendingTcpSocket();
s->startStreams();
const size_t id = _nextConnectionId++;
auto p = std::make_shared<Peer>(Peer{
id,
"",
ParallelConnection(std::move(s)),
ParallelConnection::Status::Connecting,
std::thread()
});
auto it = _peers.emplace(p->id, p);
it.first->second->thread = std::thread([this, id]() { handlePeer(id); });
}
}
std::shared_ptr<ParallelServer::Peer> ParallelServer::peer(size_t id) {
std::lock_guard lock(_peerListMutex);
const auto it = _peers.find(id);
if (it == _peers.end()) {
return nullptr;
}
return it->second;
}
void ParallelServer::handlePeer(size_t id) {
while (!_shouldStop) {
std::shared_ptr<Peer> p = peer(id);
if (!p) {
return;
}
if (!p->parallelConnection.isConnectedOrConnecting()) {
return;
}
try {
ParallelConnection::Message m = p->parallelConnection.receiveMessage();
PeerMessage msg;
msg.peerId = id;
msg.message = m;
_incomingMessages.push(msg);
}
catch (const ParallelConnection::ConnectionLostError&) {
LERROR(fmt::format("Connection lost to {}", p->id));
PeerMessage msg;
msg.peerId = id;
msg.message = ParallelConnection::Message(
ParallelConnection::MessageType::Disconnection, std::vector<char>()
);
_incomingMessages.push(msg);
return;
}
}
}
void ParallelServer::eventLoop() {
while (!_shouldStop) {
PeerMessage pm = _incomingMessages.pop();
handlePeerMessage(std::move(pm));
}
}
void ParallelServer::handlePeerMessage(PeerMessage peerMessage) {
const size_t peerId = peerMessage.peerId;
auto it = _peers.find(peerId);
if (it == _peers.end()) {
return;
}
std::shared_ptr<Peer>& peer = it->second;
const ParallelConnection::MessageType type = peerMessage.message.type;
std::vector<char>& data = peerMessage.message.content;
switch (type) {
case ParallelConnection::MessageType::Authentication:
handleAuthentication(peer, std::move(data));
break;
case ParallelConnection::MessageType::Data:
handleData(*peer, std::move(data));
break;
case ParallelConnection::MessageType::HostshipRequest:
handleHostshipRequest(peer, std::move(data));
break;
case ParallelConnection::MessageType::HostshipResignation:
handleHostshipResignation(*peer);
break;
case ParallelConnection::MessageType::Disconnection:
disconnect(*peer);
break;
default:
LERROR(fmt::format("Unsupported message type: {}", static_cast<int>(type)));
break;
}
}
void ParallelServer::handleAuthentication(std::shared_ptr<Peer> peer,
std::vector<char> message)
{
std::stringstream input(std::string(message.begin(), message.end()));
// 8 bytes passcode
uint64_t passwordHash = 0;
input.read(reinterpret_cast<char*>(&passwordHash), sizeof(uint64_t));
if (passwordHash != _passwordHash) {
LERROR(fmt::format("Connection {} provided incorrect passcode.", peer->id));
disconnect(*peer);
return;
}
// 4 bytes name size
uint32_t nameSize = 0;
input.read(reinterpret_cast<char*>(&nameSize), sizeof(uint32_t));
// <nameSize> bytes name
std::string name(nameSize, static_cast<char>(0));
input.read(&name[0], nameSize);
if (nameSize == 0) {
name = "Anonymous";
}
setName(*peer, name);
LINFO(fmt::format("Connection established with {} ('{}')", peer->id, name));
std::string defaultHostAddress;
{
std::lock_guard _hostMutex(_hostInfoMutex);
defaultHostAddress = _defaultHostAddress;
}
if (_hostPeerId == 0 &&
peer->parallelConnection.socket()->address() == defaultHostAddress)
{
// Directly promote the conenction to host (initialize) if there is no host, and
// ip matches default host ip.
LINFO(fmt::format("Connection {} directly promoted to host", peer->id));
assignHost(peer);
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
// sendConnectionStatus(it->second) ?
sendConnectionStatus(*peer);
}
}
else {
setToClient(*peer);
}
setNConnections(nConnections() + 1);
}
void ParallelServer::handleData(const Peer& peer, std::vector<char> data) {
if (peer.id != _hostPeerId) {
LINFO(fmt::format(
"Ignoring connection {} trying to send data without being host", peer.id
));
}
sendMessageToClients(ParallelConnection::MessageType::Data, data);
}
void ParallelServer::handleHostshipRequest(std::shared_ptr<Peer> peer,
std::vector<char> message)
{
std::stringstream input(std::string(message.begin(), message.end()));
LINFO(fmt::format("Connection {} requested hostship", peer->id));
uint64_t passwordHash = 0;
input.read(reinterpret_cast<char*>(&passwordHash), sizeof(uint64_t));
if (passwordHash != _changeHostPasswordHash) {
LERROR(fmt::format("Connection {} provided incorrect host password", peer->id));
return;
}
size_t oldHostPeerId = 0;
{
std::lock_guard lock(_hostInfoMutex);
oldHostPeerId = _hostPeerId;
}
if (oldHostPeerId == peer->id) {
LINFO(fmt::format("Connection {} is already the host", peer->id));
return;
}
assignHost(peer);
LINFO(fmt::format("Switched host from {} to {}", oldHostPeerId, peer->id));
}
void ParallelServer::handleHostshipResignation(Peer& peer) {
LINFO(fmt::format("Connection {} wants to resign its hostship", peer.id));
setToClient(peer);
LINFO(fmt::format("Connection {} resigned as host", peer.id));
}
bool ParallelServer::isConnected(const Peer& peer) const {
return peer.status != ParallelConnection::Status::Connecting &&
peer.status != ParallelConnection::Status::Disconnected;
}
void ParallelServer::sendMessage(Peer& peer, ParallelConnection::MessageType messageType,
const std::vector<char>& message)
{
peer.parallelConnection.sendMessage({ messageType, message });
}
void ParallelServer::sendMessageToAll(ParallelConnection::MessageType messageType,
const std::vector<char>& message)
{
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
if (isConnected(*it.second)) {
it.second->parallelConnection.sendMessage({ messageType, message });
}
}
}
void ParallelServer::sendMessageToClients(ParallelConnection::MessageType messageType,
const std::vector<char>& message)
{
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
if (it.second->status == ParallelConnection::Status::ClientWithHost) {
it.second->parallelConnection.sendMessage({ messageType, message });
}
}
}
void ParallelServer::disconnect(Peer& peer) {
if (isConnected(peer)) {
setNConnections(nConnections() - 1);
}
size_t hostPeerId = 0;
{
std::lock_guard lock(_hostInfoMutex);
hostPeerId = _hostPeerId;
}
// Make sure any disconnecting host is first degraded to client, in order to notify
// other clients about host disconnection.
if (peer.id == hostPeerId) {
setToClient(peer);
}
peer.parallelConnection.disconnect();
peer.thread.join();
_peers.erase(peer.id);
}
void ParallelServer::setName(Peer& peer, std::string name) {
peer.name = std::move(name);
size_t hostPeerId = 0;
{
std::lock_guard lock(_hostInfoMutex);
hostPeerId = _hostPeerId;
}
// Make sure everyone gets the new host name.
if (peer.id == hostPeerId) {
{
std::lock_guard lock(_hostInfoMutex);
_hostName = peer.name;
}
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
// sendConnectionStatus(it->second) ?
sendConnectionStatus(peer);
}
}
}
void ParallelServer::assignHost(std::shared_ptr<Peer> newHost) {
{
std::lock_guard lock(_hostInfoMutex);
std::shared_ptr<ParallelServer::Peer> oldHost = peer(_hostPeerId);
if (oldHost) {
oldHost->status = ParallelConnection::Status::ClientWithHost;
}
_hostPeerId = newHost->id;
_hostName = newHost->name;
}
newHost->status = ParallelConnection::Status::Host;
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
if (it.second != newHost) {
it.second->status = ParallelConnection::Status::ClientWithHost;
}
sendConnectionStatus(*it.second);
}
}
void ParallelServer::setToClient(Peer& peer) {
if (peer.status == ParallelConnection::Status::Host) {
{
std::lock_guard lock(_hostInfoMutex);
_hostPeerId = 0;
_hostName.clear();
}
// If host becomes client, make all clients hostless.
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
it.second->status = ParallelConnection::Status::ClientWithoutHost;
sendConnectionStatus(*it.second);
}
}
else {
peer.status = (_hostPeerId > 0) ?
ParallelConnection::Status::ClientWithHost :
ParallelConnection::Status::ClientWithoutHost;
sendConnectionStatus(peer);
}
}
void ParallelServer::setNConnections(size_t nConnections) {
_nConnections = nConnections;
std::vector<char> data;
const uint32_t n = static_cast<uint32_t>(_nConnections);
data.insert(
data.end(),
reinterpret_cast<const char*>(&n),
reinterpret_cast<const char*>(&n) + sizeof(uint32_t)
);
sendMessageToAll(ParallelConnection::MessageType::NConnections, data);
}
void ParallelServer::sendConnectionStatus(Peer& peer) {
std::vector<char> data;
const uint32_t outStatus = static_cast<uint32_t>(peer.status);
data.insert(
data.end(),
reinterpret_cast<const char*>(&outStatus),
reinterpret_cast<const char*>(&outStatus) + sizeof(uint32_t)
);
const uint32_t outHostNameSize = static_cast<uint32_t>(_hostName.size());
data.insert(
data.end(),
reinterpret_cast<const char*>(&outHostNameSize),
reinterpret_cast<const char*>(&outHostNameSize) + sizeof(uint32_t)
);
data.insert(
data.end(),
_hostName.data(),
_hostName.data() + outHostNameSize
);
sendMessage(peer, ParallelConnection::MessageType::ConnectionStatus, data);
}
size_t ParallelServer::nConnections() const {
return _nConnections;
}
} // namespace openspace
+2 -1
View File
@@ -1072,7 +1072,8 @@ scripting::LuaLibrary RenderEngine::luaLibrary() {
{
codegen::lua::AddScreenSpaceRenderable,
codegen::lua::RemoveScreenSpaceRenderable,
codegen::lua::TakeScreenshot
codegen::lua::TakeScreenshot,
codegen::lua::DpiScaling
}
};
}
+6
View File
@@ -67,6 +67,12 @@ namespace {
return static_cast<int>(screenshotNumber);
}
// Extracts the DPI scaling for either the GUI window or if there is no dedicated GUI
// window, the first window.
[[codegen::luawrap]] float dpiScaling() {
return openspace::global::windowDelegate->osDpiScaling();
}
#include "renderengine_lua_codegen.cpp"
} // namespace
+4
View File
@@ -343,6 +343,10 @@ bool AssetManager::loadAsset(Asset* asset, Asset* parent) {
LERROR(fmt::format("Could not load asset {}: {}", asset->path(), e.message));
return false;
}
catch (const ghoul::RuntimeError& e) {
LERRORC(e.component, e.message);
return false;
}
// Extract meta information from the asset file if it was provided
lua_getglobal(*_luaState, AssetGlobalVariableName);
+1 -1
View File
@@ -61,7 +61,7 @@ namespace {
* Returns the paths to all loaded assets, loaded directly or indirectly, as a table
* containing the paths to all loaded assets.
*/
[[codegen::luawrap]] std::vector<std::string> allAssets(std::string assetName) {
[[codegen::luawrap]] std::vector<std::string> allAssets() {
using namespace openspace;
std::vector<const Asset*> as = global::openSpaceEngine->assetManager().allAssets();
std::vector<std::string> res;
@@ -34,7 +34,7 @@ namespace {
OS os = CpuCap.operatingSystem();
switch (os) {
case OS::Windows10:
case OS::Windows10or11:
case OS::WindowsServer2016:
case OS::WindowsVista:
case OS::WindowsServer2008:
+11 -11
View File
@@ -896,24 +896,24 @@ bool TimeManager::isPlayingBackSessionRecording() const {
}
void TimeManager::setTimeFromProfile(const Profile& p) {
Time t;
if (p.time.has_value()) {
switch (p.time.value().type) {
case Profile::Time::Type::Relative:
t.setTimeRelativeFromProfile(p.time.value().value);
break;
case Profile::Time::Type::Relative:
Time::setTimeRelativeFromProfile(p.time.value().value);
break;
case Profile::Time::Type::Absolute:
t.setTimeAbsoluteFromProfile(p.time.value().value);
break;
case Profile::Time::Type::Absolute:
Time::setTimeAbsoluteFromProfile(p.time.value().value);
break;
default:
throw ghoul::MissingCaseException();
default:
throw ghoul::MissingCaseException();
}
}
else {
throw ghoul::RuntimeError("No 'time' entry exists in the startup profile");
// No value was specified so we are using 'now' instead
std::string now = std::string(Time::now().UTC());
Time::setTimeAbsoluteFromProfile(now);
}
}