Merge branch 'master' into tl_api

This commit is contained in:
Aidan Clear 2019-01-29 13:07:48 +00:00 committed by GitHub
commit cb6c57d24e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 2033 additions and 933 deletions

View File

@ -4,16 +4,23 @@
* Upgraded Boost to 1.69.0
* Added point transformation functionality for LibCarla and PythonAPI
* Added "sensor_tick" attribute to sensors (cameras and lidars) to specify the capture rate in seconds
* Added Export/Import map tools
* Added "get_forward_vector()" to rotation and transform, retrieves the unit vector on the rotation's X-axis
* Added support for Deepin in PythonAPI's setup.py
* Added support for spawning and controlling walkers (pedestrians)
* Renamed vehicle.get_vehicle_control() to vehicle.get_control() to be consistent with walkers
* Remove crash reporter from packaged build
* Added a few methods to manage an actor:
- set_velocity: for setting the linear velocity
- set_angular_velocity: for setting the angular velocity
- get_angular_velocity: for getting the angular velocity
- add_impulse: for applying an impulse (in world axis)
* Added support for gnss_sensor
* Fixed TCP accept error, too many open files while creating and destroying a lot of sensors
* Fixed lost error messages in client-side, now when a request fails it reports the reason
* Improved simulator fatal error handling, now uses UE4 fatal error system
* Fixed crash when an actor was destroyed but not de-registered, e.g. falling out of world bounds
* LibCarla server pipeline now compiles with exceptions disabled for better performance and compatibility with UE4
* OpenDriveActor has been rewritten using the Waypoint API, this has fixed some bugs
## CARLA 0.9.2

View File

@ -28,10 +28,13 @@ C++
rare occasions if it results in clearer code.
* Compilation should not give any error or warning
(`clang++-6.0 -Wall -Wextra -std=C++14 -Wno-missing-braces`).
* The use of `throw` is forbidden, use `carla::throw_exception` instead.
* Unreal C++ code (CarlaUE4 and Carla plugin) follow the
[Unreal Engine's Coding Standard][ue4link] with the exception of using
spaces instead of tabs.
* LibCarla uses a variation of [Google's style guide][googlelink].
* Uses of `try-catch` blocks should be surrounded by
`#ifndef LIBCARLA_NO_EXCEPTIONS` if the code is used in the server-side.
[ue4link]: https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/
[googlelink]: https://google.github.io/styleguide/cppguide.html

View File

@ -0,0 +1,44 @@
<h1>Importing and exporting maps for distribution builds</h1>
!!! important
The given scripts only works if the files are kept in the folders
detailed below and on Linux.
This method and its scripts are aimed to reducing the size of the distribution
build, allowing to import maps on distributed builds with a script, exporting
them with yet another script from the editor.
<h4>How to export a map</h4>
The current script only work for content placed into the
"CarlaRoot/Unreal/CarlaUE4/Content/Carla/ExportedMaps" folder, inside the unreal's project
structure. Once all the changes are saved in the editor and the maps to export
are located in that folder, running
```sh
make export-maps
```
will clean, cook and pack the necessary assets with the maps. Please, note that
there is no way of knowing what assets are on the project-to-import-to, so the
final package will usually contain assets needed for the current project to work
even though they might not be directly used in the exported map. Also, the maps
referenced inside the Editor Properties will be exported due to the current way
of cooking provided by Unreal.
!!! important
This will only work if "Use pak file" is unchecked under the Packaging settings
Once the script is completed, the packed file can be found under ExportedMaps in
the project's root.
<h4>How to import a map</h4>
In order to do so, the file generated in the import phase has to be placed in a folder
at the root of the distributed folder called "ExportedMaps". Once the zip is placed there,
by running
```sh
./ImportMaps.sh
```
the content will get extracted and located properly where the executable can reach it. All
the imported maps can be loaded by passing the convenient argument to the CarlaUE4.sh script.

View File

@ -26,6 +26,7 @@
* [Running in a Docker](carla_docker.md)
* [How to make a new map with RoadRunner](how_to_make_a_new_map.md)
* [How to link Epic's Automotive Materials](epic_automotive_materials.md)
* [How to export and import maps to distribution builds](export_import_dist.md)
<h3>Contributing</h3>

View File

@ -13,10 +13,11 @@ if (CMAKE_BUILD_TYPE STREQUAL "Client")
add_subdirectory("client")
elseif (CMAKE_BUILD_TYPE STREQUAL "Server")
add_subdirectory("server")
if (NOT WIN32) # TODO(Andrei): Fix compilation for Windows
add_subdirectory("test")
endif()
else ()
message(FATAL_ERROR "Unknown build type '${CMAKE_BUILD_TYPE}'")
endif ()
# GTest is not compiled on Windows.
if (NOT WIN32)
add_subdirectory("test")
endif()

View File

@ -77,29 +77,35 @@ endif()
# carla_server library.
file(GLOB_RECURSE libcarla_server_sources
"${libcarla_source_path}/carla/*.cpp"
file(GLOB libcarla_server_sources
"${libcarla_source_path}/carla/*.h"
"${libcarla_source_path}/carla/Buffer.cpp"
"${libcarla_source_path}/carla/Exception.cpp"
"${libcarla_source_path}/carla/geom/*.cpp"
"${libcarla_source_path}/carla/geom/*.h"
"${libcarla_source_path}/carla/opendrive/*.cpp"
"${libcarla_source_path}/carla/opendrive/*.h"
"${libcarla_source_path}/carla/map/*.cpp"
"${libcarla_source_path}/carla/map/*.h"
"${libcarla_source_path}/carla/map/element/*.cpp"
"${libcarla_source_path}/carla/map/element/*.h"
"${libcarla_source_path}/carla/map/element/cephes/*.cpp"
"${libcarla_source_path}/carla/map/element/cephes/*.h"
"${libcarla_source_path}/carla/opendrive/parser/*.cpp"
"${libcarla_source_path}/carla/opendrive/parser/*.h"
"${libcarla_source_path}/carla/opendrive/parser/pugixml/*.cpp"
"${libcarla_source_path}/carla/opendrive/parser/pugixml/*.hpp"
"${libcarla_source_path}/carla/road/*.cpp"
"${libcarla_source_path}/carla/road/*.h"
"${libcarla_source_path}/carla/road/element/*.cpp"
"${libcarla_source_path}/carla/road/element/*.h"
"${libcarla_source_path}/carla/road/element/cephes/*.cpp"
"${libcarla_source_path}/carla/road/element/cephes/*.h"
"${libcarla_source_path}/carla/rpc/*.cpp"
"${libcarla_source_path}/carla/rpc/*.h"
"${libcarla_source_path}/carla/sensor/*.cpp"
"${libcarla_source_path}/carla/sensor/*.h"
"${libcarla_source_path}/carla/sensor/s11n/*.cpp"
"${libcarla_source_path}/carla/sensor/s11n/*.h"
"${libcarla_source_path}/carla/streaming/*.cpp"
"${libcarla_source_path}/carla/streaming/*.h")
"${libcarla_source_path}/carla/sensor/s11n/SensorHeaderSerializer.cpp"
"${libcarla_source_path}/carla/streaming/*.h"
"${libcarla_source_path}/carla/streaming/detail/*.cpp"
"${libcarla_source_path}/carla/streaming/detail/*.h"
"${libcarla_source_path}/carla/streaming/detail/tcp/*.cpp"
"${libcarla_source_path}/carla/streaming/detail/tcp/*.h"
"${libcarla_source_path}/carla/streaming/low_level/*.h")
# Create targets for debug and release in the same build type.
foreach(target carla_server_debug carla_server)

View File

@ -1,18 +1,30 @@
cmake_minimum_required(VERSION 3.5.1)
project(libcarla-unit-tests)
file(GLOB_RECURSE libcarla_test_sources
"${libcarla_source_path}/carla/profiler/*.h"
"${libcarla_source_path}/carla/profiler/*.cpp"
"${libcarla_source_path}/test/*.h"
"${libcarla_source_path}/test/*.cpp")
if (CMAKE_BUILD_TYPE STREQUAL "Client")
set(carla_config client)
elseif (CMAKE_BUILD_TYPE STREQUAL "Server")
set(carla_config server)
endif ()
link_directories(
${RPCLIB_LIB_PATH}
${GTEST_LIB_PATH})
file(GLOB libcarla_test_sources
"${libcarla_source_path}/carla/profiler/*.cpp"
"${libcarla_source_path}/carla/profiler/*.h"
"${libcarla_source_path}/test/*.cpp"
"${libcarla_source_path}/test/*.h"
"${libcarla_source_path}/test/${carla_config}/*.cpp"
"${libcarla_source_path}/test/${carla_config}/*.h"
"${libcarla_source_path}/test/common/*.cpp"
"${libcarla_source_path}/test/common/*.h")
file(GLOB libcarla_test_client_sources "")
# Create targets for debug and release in the same build type.
foreach(target libcarla_test_debug libcarla_test_release)
foreach(target libcarla_test_${carla_config}_debug libcarla_test_${carla_config}_release)
add_executable(${target} ${libcarla_test_sources})
target_compile_definitions(${target} PUBLIC
@ -22,7 +34,8 @@ foreach(target libcarla_test_debug libcarla_test_release)
target_include_directories(${target} PRIVATE
"${BOOST_INCLUDE_PATH}"
"${RPCLIB_INCLUDE_PATH}"
"${GTEST_INCLUDE_PATH}")
"${GTEST_INCLUDE_PATH}"
"${libcarla_source_path}/test")
if (WIN32)
target_link_libraries(${target} "gtest_main.lib")
@ -38,10 +51,10 @@ foreach(target libcarla_test_debug libcarla_test_release)
endforeach(target)
# Specific options for debug.
set_target_properties(libcarla_test_debug PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_DEBUG})
target_link_libraries(libcarla_test_debug "carla_server_debug")
target_compile_definitions(libcarla_test_debug PUBLIC -DBOOST_ASIO_ENABLE_BUFFER_DEBUGGING)
set_target_properties(libcarla_test_${carla_config}_debug PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_DEBUG})
target_link_libraries(libcarla_test_${carla_config}_debug "carla_${carla_config}_debug")
target_compile_definitions(libcarla_test_${carla_config}_debug PUBLIC -DBOOST_ASIO_ENABLE_BUFFER_DEBUGGING)
# Specific options for release.
set_target_properties(libcarla_test_release PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_RELEASE})
target_link_libraries(libcarla_test_release "carla_server")
set_target_properties(libcarla_test_${carla_config}_release PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_RELEASE})
target_link_libraries(libcarla_test_${carla_config}_release "carla_${carla_config}")

View File

@ -7,6 +7,7 @@
#pragma once
#include "carla/Debug.h"
#include "carla/Exception.h"
#include "carla/Logging.h"
#include <boost/asio/buffer.hpp>
@ -72,7 +73,9 @@ namespace carla {
/// @copydoc Buffer(size_type)
explicit Buffer(uint64_t size)
: Buffer([size]() {
DEBUG_ASSERT(size <= max_size());
if (size > max_size()) {
throw_exception(std::invalid_argument("message size too big"));
}
return static_cast<size_type>(size);
} ()) {}
@ -245,7 +248,9 @@ namespace carla {
/// @copydoc reset(size_type)
void reset(uint64_t size) {
DEBUG_ASSERT(size <= max_size());
if (size > max_size()) {
throw_exception(std::invalid_argument("message size too big"));
}
reset(static_cast<size_type>(size));
}

View File

@ -0,0 +1,50 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "carla/Exception.h"
// =============================================================================
// -- Define boost::throw_exception --------------------------------------------
// =============================================================================
#ifdef BOOST_NO_EXCEPTIONS
namespace boost {
void throw_exception(const std::exception &e) {
carla::throw_exception(e);
}
} // namespace boost
#endif // BOOST_NO_EXCEPTIONS
// =============================================================================
// -- Workaround for Boost.Asio bundled with rpclib ----------------------------
// =============================================================================
#ifdef ASIO_NO_EXCEPTIONS
#include <exception>
#include <system_error>
#include <typeinfo>
namespace clmdep_asio {
namespace detail {
template <typename Exception>
void throw_exception(const Exception& e) {
carla::throw_exception(e);
}
template void throw_exception<std::bad_cast>(const std::bad_cast &);
template void throw_exception<std::exception>(const std::exception &);
template void throw_exception<std::system_error>(const std::system_error &);
} // namespace detail
} // namespace clmdep_asio
#endif // ASIO_NO_EXCEPTIONS

View File

@ -0,0 +1,43 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#ifdef LIBCARLA_NO_EXCEPTIONS
namespace std {
class exception;
} // namespace std
namespace carla {
/// User define function, similar to Boost throw_exception.
///
/// @important Boost exceptions are also routed to this function.
///
/// When compiled with LIBCARLA_NO_EXCEPTIONS, this function is left undefined
/// in LibCarla, and the modules using LibCarla are expected to supply an
/// appropriate definition. Callers of throw_exception are allowed to assume
/// that the function never returns; therefore, if the user-defined
/// throw_exception returns, the behavior is undefined.
[[ noreturn ]] void throw_exception(const std::exception &e);
} // namespace carla
#else
namespace carla {
template <typename T>
[[ noreturn ]] void throw_exception(const T &e) {
throw e;
}
} // namespace carla
#endif // LIBCARLA_NO_EXCEPTIONS

View File

@ -0,0 +1,162 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/Exception.h"
#include "carla/MsgPack.h"
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <tuple>
namespace clmdep_msgpack {
MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {
namespace adaptor {
// ===========================================================================
// -- Adaptors for boost::optional -------------------------------------------
// ===========================================================================
template<typename T>
struct convert<boost::optional<T>> {
const clmdep_msgpack::object &operator()(
const clmdep_msgpack::object &o,
boost::optional<T> &v) const {
if (o.type != clmdep_msgpack::type::ARRAY) {
::carla::throw_exception(clmdep_msgpack::type_error());
}
if (o.via.array.size == 1) {
v.reset();
} else if (o.via.array.size == 2) {
v.reset(o.via.array.ptr[1].as<T>());
} else {
::carla::throw_exception(clmdep_msgpack::type_error());
}
return o;
}
};
template<typename T>
struct pack<boost::optional<T>> {
template <typename Stream>
packer<Stream> &operator()(
clmdep_msgpack::packer<Stream> &o,
const boost::optional<T> &v) const {
if (v.has_value()) {
o.pack_array(2);
o.pack(true);
o.pack(*v);
} else {
o.pack_array(1);
o.pack(false);
}
return o;
}
};
template<typename T>
struct object_with_zone<boost::optional<T>> {
void operator()(
clmdep_msgpack::object::with_zone &o,
const boost::optional<T> &v) const {
o.type = type::ARRAY;
if (v.has_value()) {
o.via.array.size = 2;
o.via.array.ptr = static_cast<clmdep_msgpack::object*>(o.zone.allocate_align(
sizeof(clmdep_msgpack::object) * o.via.array.size,
MSGPACK_ZONE_ALIGNOF(clmdep_msgpack::object)));
o.via.array.ptr[0] = clmdep_msgpack::object(true, o.zone);
o.via.array.ptr[1] = clmdep_msgpack::object(*v, o.zone);
} else {
o.via.array.size = 1;
o.via.array.ptr = static_cast<clmdep_msgpack::object*>(o.zone.allocate_align(
sizeof(clmdep_msgpack::object) * o.via.array.size,
MSGPACK_ZONE_ALIGNOF(clmdep_msgpack::object)));
o.via.array.ptr[0] = clmdep_msgpack::object(false, o.zone);
}
}
};
// ===========================================================================
// -- Adaptors for boost::variant --------------------------------------------
// ===========================================================================
template<typename... Ts>
struct convert<boost::variant<Ts...>> {
const clmdep_msgpack::object &operator()(
const clmdep_msgpack::object &o,
boost::variant<Ts...> &v) const {
if (o.type != clmdep_msgpack::type::ARRAY) {
::carla::throw_exception(clmdep_msgpack::type_error());
}
if (o.via.array.size != 2) {
::carla::throw_exception(clmdep_msgpack::type_error());
}
const auto index = o.via.array.ptr[0].as<int>();
copy_to_variant(index, o, v, std::make_index_sequence<sizeof...(Ts)>());
return o;
}
private:
template <size_t I>
static void copy_to_variant_impl(
const clmdep_msgpack::object &o,
boost::variant<Ts...> &v) {
/// @todo Workaround for finding the type.
auto dummy = std::get<I>(std::tuple<Ts...>{});
using T = decltype(dummy);
v = o.via.array.ptr[1].as<T>();
}
template <size_t... Is>
static void copy_to_variant(
const size_t index,
const clmdep_msgpack::object &o,
boost::variant<Ts...> &v,
std::index_sequence<Is...>) {
std::initializer_list<int> ({
(index == Is ? copy_to_variant_impl<Is>(o, v), 0 : 0)...
});
}
};
template<typename... Ts>
struct pack<boost::variant<Ts...>> {
template <typename Stream>
packer<Stream> &operator()(
clmdep_msgpack::packer<Stream> &o,
const boost::variant<Ts...> &v) const {
o.pack_array(2);
o.pack(static_cast<int>(v.which()));
boost::apply_visitor([&](const auto &value) { o.pack(value); }, v);
return o;
}
};
template<typename... Ts>
struct object_with_zone<boost::variant<Ts...>> {
void operator()(
clmdep_msgpack::object::with_zone &o,
const boost::variant<Ts...> &v) const {
o.type = type::ARRAY;
o.via.array.size = 2;
o.via.array.ptr = static_cast<clmdep_msgpack::object*>(o.zone.allocate_align(
sizeof(clmdep_msgpack::object) * o.via.array.size,
MSGPACK_ZONE_ALIGNOF(clmdep_msgpack::object)));
o.via.array.ptr[0] = clmdep_msgpack::object(static_cast<int>(v.which()), o.zone);
boost::apply_visitor([&](const auto &value) {
o.via.array.ptr[1] = clmdep_msgpack::object(value, o.zone);
}, v);
}
};
} // namespace adaptor
} // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS)
} // namespace msgpack

View File

@ -1,36 +0,0 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include <boost/optional.hpp>
namespace carla {
template <typename T>
class Optional : private boost::optional<T> {
using parent_type = boost::optional<T>;
public:
template <typename... Args>
explicit Optional(Args &&... args)
: parent_type(std::forward<Args>(args)...) {}
using parent_type::operator*;
using parent_type::operator->;
using parent_type::operator=;
using parent_type::emplace;
using parent_type::reset;
using parent_type::swap;
// using parent_type::value; disabled to avoid exceptions.
using parent_type::value_or;
bool has_value() const {
return parent_type::is_initialized();
}
};
} // namespace carla

View File

@ -6,6 +6,7 @@
#pragma once
#include "carla/Exception.h"
#include "carla/Time.h"
#include <boost/variant.hpp>
@ -99,10 +100,12 @@ namespace detail {
std::unique_lock<std::mutex> lock(_mutex);
auto &r = _map[&detail::thread_tag];
r.should_wait = true;
if (!_cv.wait_for(lock, timeout.to_chrono(), [&]() { return !r.should_wait; }))
throw std::runtime_error("RecurrentSharedFuture.WaitFor: time-out");
if (r.value.which() == 1)
throw boost::get<SharedException>(r.value);
if (!_cv.wait_for(lock, timeout.to_chrono(), [&]() { return !r.should_wait; })) {
throw_exception(std::runtime_error("RecurrentSharedFuture.WaitFor: time-out"));
}
if (r.value.which() == 1) {
throw_exception(boost::get<SharedException>(r.value));
}
return boost::get<T>(std::move(r.value));
}

View File

@ -6,16 +6,17 @@
#include "carla/client/ActorAttribute.h"
#include "carla/Exception.h"
#include "carla/Logging.h"
#include "carla/StringUtil.h"
namespace carla {
namespace client {
#define LIBCARLA_THROW_INVALID_VALUE(message) throw InvalidAttributeValue(GetId() + ": " + message);
#define LIBCARLA_THROW_INVALID_VALUE(message) throw_exception(InvalidAttributeValue(GetId() + ": " + message));
#define LIBCARLA_THROW_BAD_VALUE_CAST(type) \
if (GetType() != rpc::ActorAttributeType:: type) { \
throw BadAttributeCast(GetId() + ": bad attribute cast: cannot convert to " #type); \
throw_exception(BadAttributeCast(GetId() + ": bad attribute cast: cannot convert to " #type)); \
}
void ActorAttribute::Set(std::string value) {

View File

@ -6,6 +6,7 @@
#include "carla/client/ActorBlueprint.h"
#include "carla/Exception.h"
#include "carla/StringUtil.h"
#include <algorithm>
@ -41,7 +42,7 @@ namespace client {
auto it = _attributes.find(id);
if (it == _attributes.end()) {
using namespace std::string_literals;
throw std::out_of_range("attribute '"s + id + "' not found");
throw_exception(std::out_of_range("attribute '"s + id + "' not found"));
}
return it->second;
}

View File

@ -6,6 +6,8 @@
#include "carla/client/BlueprintLibrary.h"
#include "carla/Exception.h"
#include <algorithm>
#include <iterator>
@ -40,14 +42,15 @@ namespace client {
auto it = _blueprints.find(key);
if (it == _blueprints.end()) {
using namespace std::string_literals;
throw std::out_of_range("blueprint '"s + key + "' not found");
throw_exception(std::out_of_range("blueprint '"s + key + "' not found"));
}
return it->second;
}
BlueprintLibrary::const_reference BlueprintLibrary::at(size_type pos) const {
if (pos >= size())
throw std::out_of_range("index out of range");
if (pos >= size()) {
throw_exception(std::out_of_range("index out of range"));
}
return operator[](pos);
}

View File

@ -71,7 +71,7 @@ namespace client {
}
if (GetParent() == nullptr) {
throw std::runtime_error(GetDisplayId() + ": not attached to vehicle");
throw_exception(std::runtime_error(GetDisplayId() + ": not attached to vehicle"));
return;
}

View File

@ -75,7 +75,7 @@ namespace client {
}
void LaneDetector::Stop() {
throw std::runtime_error("LaneDetector::Stop(): not implemented.");
throw_exception(std::runtime_error("LaneDetector::Stop(): not implemented."));
}
SharedPtr<sensor::SensorData> LaneDetector::TickLaneDetector(

View File

@ -31,7 +31,7 @@ namespace client {
const geom::Location &location,
bool project_to_road) const {
DEBUG_ASSERT(_map != nullptr);
Optional<road::element::Waypoint> waypoint;
boost::optional<road::element::Waypoint> waypoint;
if (project_to_road) {
waypoint = _map->GetClosestWaypointOnRoad(location);
} else {

View File

@ -55,12 +55,7 @@ namespace client {
const ActorBlueprint &blueprint,
const geom::Transform &transform,
Actor *parent_actor) {
try {
return _episode.Lock()->SpawnActor(blueprint, transform, parent_actor);
} catch (const std::exception &e) {
log_warning("SpawnActor: failed with:", e.what());
throw;
}
return _episode.Lock()->SpawnActor(blueprint, transform, parent_actor);
}
SharedPtr<Actor> World::TrySpawnActor(
@ -69,7 +64,7 @@ namespace client {
Actor *parent_actor) noexcept {
try {
return SpawnActor(blueprint, transform, parent_actor);
} catch (const std::exception &) {
} catch (const std::exception &e) {
return nullptr;
}
}

View File

@ -10,6 +10,7 @@
#include "carla/rpc/ActorDescription.h"
#include "carla/rpc/Client.h"
#include "carla/rpc/DebugShape.h"
#include "carla/rpc/Response.h"
#include "carla/rpc/VehicleControl.h"
#include "carla/rpc/WalkerControl.h"
#include "carla/streaming/Client.h"
@ -21,6 +22,15 @@ namespace carla {
namespace client {
namespace detail {
template <typename T>
static T Get(carla::rpc::Response<T> &response) {
return response.Get();
}
static bool Get(carla::rpc::Response<void> &) {
return true;
}
// ===========================================================================
// -- Client::Pimpl ----------------------------------------------------------
// ===========================================================================
@ -36,8 +46,14 @@ namespace detail {
}
template <typename T, typename... Args>
T CallAndWait(const std::string &function, Args &&... args) {
return rpc_client.call(function, std::forward<Args>(args)...).template as<T>();
auto CallAndWait(const std::string &function, Args &&... args) {
auto object = rpc_client.call(function, std::forward<Args>(args)...);
using R = typename carla::rpc::Response<T>;
auto response = object.template as<R>();
if (response.HasError()) {
throw_exception(std::runtime_error(response.GetError().What()));
}
return Get(response);
}
template <typename... Args>
@ -122,7 +138,13 @@ namespace detail {
}
bool Client::DestroyActor(const rpc::Actor &actor) {
return _pimpl->CallAndWait<bool>("destroy_actor", actor);
try {
return _pimpl->CallAndWait<void>("destroy_actor", actor);
} catch (const std::exception &e) {
log_error("failed to destroy actor:", actor.id, actor.description.id);
log_error(e.what());
return false;
}
}
void Client::SetActorLocation(const rpc::Actor &actor, const geom::Location &location) {

View File

@ -6,6 +6,7 @@
#include "carla/client/detail/EpisodeProxy.h"
#include "carla/Exception.h"
#include "carla/client/detail/Simulator.h"
#include <exception>
@ -38,14 +39,14 @@ namespace detail {
typename EpisodeProxyImpl<T>::SharedPtrType EpisodeProxyImpl<T>::Lock() const {
auto ptr = Load(_simulator);
if (ptr == nullptr) {
throw std::runtime_error(
throw_exception(std::runtime_error(
"trying to operate on a destroyed actor; an actor's function "
"was called, but the actor is already destroyed.");
"was called, but the actor is already destroyed."));
}
if (_episode_id != ptr->GetCurrentEpisodeId()) {
throw std::runtime_error(
throw_exception(std::runtime_error(
"trying to access an expired episode; a new episode was started "
"in the simulation but an object tried accessing the old one.");
"in the simulation but an object tried accessing the old one."));
}
return ptr;
}

View File

@ -17,16 +17,16 @@ namespace road {
return Waypoint(shared_from_this(), loc);
}
Optional<Waypoint> Map::GetWaypoint(const geom::Location &loc) const {
boost::optional<Waypoint> Map::GetWaypoint(const geom::Location &loc) const {
Waypoint w = Waypoint(shared_from_this(), loc);
auto d = geom::Math::Distance2D(w.ComputeTransform().location, loc);
const RoadInfoLane *inf = _data.GetRoad(w._road_id)->GetInfo<RoadInfoLane>(w._dist);
if (d < inf->getLane(w._lane_id)->_width * 0.5) {
return Optional<Waypoint>(w);
return w;
}
return Optional<Waypoint>();
return {};
}
std::vector<element::LaneMarking> Map::CalculateCrossedLanes(

View File

@ -8,11 +8,12 @@
#include "carla/Memory.h"
#include "carla/NonCopyable.h"
#include "carla/Optional.h"
#include "carla/road/MapData.h"
#include "carla/road/element/LaneMarking.h"
#include "carla/road/element/Waypoint.h"
#include <boost/optional.hpp>
#include <vector>
namespace carla {
@ -29,7 +30,7 @@ namespace road {
element::Waypoint GetClosestWaypointOnRoad(const geom::Location &) const;
Optional<element::Waypoint> GetWaypoint(const geom::Location &) const;
boost::optional<element::Waypoint> GetWaypoint(const geom::Location &) const;
std::vector<element::LaneMarking> CalculateCrossedLanes(
const geom::Location &origin,

View File

@ -24,7 +24,8 @@ namespace rpc {
actor_id_type id = 0u;
actor_id_type parent_id;
/// @todo parent should not be here as it may change during the simulation.
actor_id_type parent_id = 0u;
ActorDescription description;

View File

@ -6,15 +6,14 @@
#pragma once
#include "carla/MsgPack.h"
#include "carla/MsgPackAdaptors.h"
#include "carla/geom/BoundingBox.h"
#include "carla/geom/Location.h"
#include "carla/geom/Rotation.h"
#include "carla/rpc/Color.h"
#include "carla/rpc/Variant.h"
// #include <rpc/msgpack/adaptor/boost/msgpack_variant.hpp>
// #include <boost/variant.hpp>
#include <boost/variant.hpp>
namespace carla {
namespace rpc {
@ -55,7 +54,7 @@ namespace rpc {
MSGPACK_DEFINE_ARRAY(location, text, draw_shadow);
};
Variant<Point, Line, Arrow, Box, String> primitive;
boost::variant<Point, Line, Arrow, Box, String> primitive;
Color color = {255u, 0u, 0u};

View File

@ -0,0 +1,138 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/MsgPack.h"
#include "carla/MsgPackAdaptors.h"
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <string>
namespace carla {
namespace rpc {
class ResponseError {
public:
ResponseError() = default;
explicit ResponseError(std::string message)
: _what(std::move(message)) {}
const std::string &What() const {
return _what;
}
MSGPACK_DEFINE_ARRAY(_what)
private:
std::string _what;
};
template <typename T>
class Response {
public:
using value_type = T;
using error_type = ResponseError;
Response() = default;
template <typename TValue>
Response(TValue &&value) : _data(std::forward<TValue>(value)) {}
template <typename TValue>
void Reset(TValue &&value) {
_data = std::forward<TValue>(value);
}
bool HasError() const {
return _data.which() == 0;
}
template <typename... Ts>
void SetError(Ts &&... args) {
_data = error_type(std::forward<Ts>(args)...);
}
const error_type &GetError() const {
DEBUG_ASSERT(HasError());
return boost::get<error_type>(_data);
}
value_type &Get() {
DEBUG_ASSERT(!HasError());
return boost::get<value_type>(_data);
}
const value_type &Get() const {
DEBUG_ASSERT(!HasError());
return boost::get<value_type>(_data);
}
operator bool() const {
return !HasError();
}
MSGPACK_DEFINE_ARRAY(_data)
private:
boost::variant<error_type, value_type> _data;
};
template <>
class Response<void> {
public:
using value_type = void;
using error_type = ResponseError;
static Response Success() {
return success_flag{};
}
Response() : _data(error_type{}) {}
Response(ResponseError error) : _data(std::move(error)) {}
bool HasError() const {
return _data.has_value();
}
template <typename... Ts>
void SetError(Ts &&... args) {
_data = error_type(std::forward<Ts>(args)...);
}
const error_type &GetError() const {
DEBUG_ASSERT(HasError());
return *_data;
}
operator bool() const {
return !HasError();
}
MSGPACK_DEFINE_ARRAY(_data)
private:
struct success_flag {};
Response(success_flag) {}
boost::optional<error_type> _data;
};
} // namespace rpc
} // namespace carla

View File

@ -1,19 +0,0 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "carla/rpc/Server.h"
#include <rpc/this_handler.h>
namespace carla {
namespace rpc {
void Server::RespondError(std::string error_message) {
::rpc::this_handler().respond_error(std::move(error_message));
}
} // namespace carla
} // namespace rpc

View File

@ -108,8 +108,6 @@ namespace detail {
_server.stop();
}
static void RespondError(std::string error_message);
private:
boost::asio::io_service _sync_io_service;

View File

@ -1,70 +0,0 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/MsgPack.h"
#include <tuple>
namespace carla {
namespace rpc {
/// @todo Workaround for packing boost::variant; it uses tuple instead so it's
/// quite inefficient memory-wise.
template <typename... Ts>
class Variant {
private:
size_t _index = 0u;
std::tuple<Ts...> _tuple;
template <typename T, typename Tuple>
struct IndexFromType;
template <typename T, typename... Types>
struct IndexFromType<T, std::tuple<T, Types...>> {
static constexpr size_t value = 0u;
};
template <typename T, typename U, typename... Types>
struct IndexFromType<T, std::tuple<U, Types...>> {
static constexpr size_t value = 1u + IndexFromType<T, std::tuple<Types...>>::value;
};
template <typename VisitorT, size_t... Is>
void ApplyVisitorImpl(VisitorT &visitor, std::index_sequence<Is...>) const {
std::initializer_list<int> ({ (_index == Is ? visitor(std::get<Is>(_tuple)), 0 : 0)... });
}
public:
Variant() = default;
template <typename ObjT>
Variant(ObjT &&rhs) {
(*this) = std::forward<ObjT>(rhs);
}
template <typename ObjT>
Variant &operator=(ObjT &&rhs) {
constexpr auto index = IndexFromType<typename std::decay<ObjT>::type, decltype(_tuple)>::value;
_index = index;
std::get<index>(_tuple) = std::forward<ObjT>(rhs);
return *this;
}
template <typename VisitorT>
void ApplyVisitor(VisitorT visitor) const {
return ApplyVisitorImpl(visitor, std::make_index_sequence<sizeof...(Ts)>());
}
MSGPACK_DEFINE_ARRAY(_index, _tuple);
};
} // namespace rpc
} // namespace carla

View File

@ -7,6 +7,7 @@
#pragma once
#include "carla/Debug.h"
#include "carla/Exception.h"
#include "carla/sensor/SensorData.h"
#include <exception>
@ -107,14 +108,14 @@ namespace data {
reference at(size_type pos) {
if (!(pos < size())) {
throw std::out_of_range("Array index out of range");
throw_exception(std::out_of_range("Array index out of range"));
}
return operator[](pos);
}
const_reference at(size_type pos) const {
if (!(pos < size())) {
throw std::out_of_range("Array index out of range");
throw_exception(std::out_of_range("Array index out of range"));
}
return operator[](pos);
}

View File

@ -6,6 +6,7 @@
#include "carla/streaming/detail/Dispatcher.h"
#include "carla/Exception.h"
#include "carla/Logging.h"
#include "carla/streaming/detail/MultiStreamState.h"
#include "carla/streaming/detail/StreamState.h"
@ -21,7 +22,7 @@ namespace detail {
auto ptr = std::make_shared<StreamStateT>(cached_token);
auto result = stream_map.emplace(std::make_pair(cached_token.get_stream_id(), ptr));
if (!result.second) {
throw std::runtime_error("failed to create stream!");
throw_exception(std::runtime_error("failed to create stream!"));
}
return ptr;
}
@ -31,11 +32,18 @@ namespace detail {
// session remaining since at this point the io_service should be already
// stopped.
for (auto &pair : _stream_map) {
#ifndef LIBCARLA_NO_EXCEPTIONS
try {
pair.second->ClearSessions();
#endif // LIBCARLA_NO_EXCEPTIONS
auto stream_state = pair.second.lock();
if (stream_state != nullptr) {
stream_state->ClearSessions();
}
#ifndef LIBCARLA_NO_EXCEPTIONS
} catch (const std::exception &e) {
log_error("failed to clear sessions:", e.what());
}
#endif // LIBCARLA_NO_EXCEPTIONS
}
}
@ -56,22 +64,36 @@ namespace detail {
std::lock_guard<std::mutex> lock(_mutex);
auto search = _stream_map.find(session->get_stream_id());
if (search != _stream_map.end()) {
DEBUG_ASSERT(search->second != nullptr);
search->second->ConnectSession(std::move(session));
return true;
} else {
log_error("Invalid session: no stream available with id", session->get_stream_id());
return false;
auto stream_state = search->second.lock();
if (stream_state != nullptr) {
stream_state->ConnectSession(std::move(session));
return true;
}
}
log_error("Invalid session: no stream available with id", session->get_stream_id());
return false;
}
void Dispatcher::DeregisterSession(std::shared_ptr<Session> session) {
DEBUG_ASSERT(session != nullptr);
std::lock_guard<std::mutex> lock(_mutex);
ClearExpiredStreams();
auto search = _stream_map.find(session->get_stream_id());
if (search != _stream_map.end()) {
DEBUG_ASSERT(search->second != nullptr);
search->second->DisconnectSession(session);
auto stream_state = search->second.lock();
if (stream_state != nullptr) {
stream_state->DisconnectSession(session);
}
}
}
void Dispatcher::ClearExpiredStreams() {
for (auto it = _stream_map.begin(); it != _stream_map.end(); ) {
if (it->second.expired()) {
it = _stream_map.erase(it);
} else {
++it;
}
}
}

View File

@ -41,6 +41,8 @@ namespace detail {
private:
void ClearExpiredStreams();
// We use a mutex here, but we assume that sessions and streams won't be
// created too often.
std::mutex _mutex;
@ -51,7 +53,7 @@ namespace detail {
/// them alive the whole run.
std::unordered_map<
stream_id_type,
std::shared_ptr<StreamStateBase>> _stream_map;
std::weak_ptr<StreamStateBase>> _stream_map;
};
} // namespace detail

View File

@ -6,6 +6,8 @@
#include "carla/streaming/detail/Token.h"
#include "carla/Exception.h"
#include <cstring>
#include <exception>
@ -21,7 +23,7 @@ namespace detail {
_token.address_type = token_data::address::ip_v6;
_token.address.v6 = addr.to_v6().to_bytes();
} else {
throw std::invalid_argument("invalid ip address!");
throw_exception(std::invalid_argument("invalid ip address!"));
}
}

View File

@ -8,6 +8,7 @@
#include "carla/BufferPool.h"
#include "carla/Debug.h"
#include "carla/Exception.h"
#include "carla/Logging.h"
#include "carla/Time.h"
@ -75,7 +76,7 @@ namespace tcp {
_connection_timer(io_service),
_buffer_pool(std::make_shared<BufferPool>()) {
if (!_token.protocol_is_tcp()) {
throw std::invalid_argument("invalid token, only TCP tokens supported");
throw_exception(std::invalid_argument("invalid token, only TCP tokens supported"));
}
}

View File

@ -44,8 +44,8 @@ namespace tcp {
auto handle_query = [this, self, callback=std::move(on_opened)](
const boost::system::error_code &ec,
size_t DEBUG_ONLY(bytes_received)) {
DEBUG_ASSERT_EQ(bytes_received, sizeof(_stream_id));
if (!ec) {
DEBUG_ASSERT_EQ(bytes_received, sizeof(_stream_id));
log_debug("session", _session_id, "for stream", _stream_id, " started");
_socket.get_io_service().post([=]() { callback(self); });
} else {

View File

@ -14,16 +14,14 @@
#endif // BOOST_ERROR_CODE_HEADER_ONLY
#ifndef BOOST_NO_EXCEPTIONS
# define BOOST_NO_EXCEPTIONS
# error LibCarla should be compiled with -DBOOST_NO_EXCEPTIONS inside UE4.
#endif // BOOST_NO_EXCEPTIONS
namespace boost {
static inline void throw_exception(const std::exception &e) {
UE_LOG(LogCarla, Fatal, TEXT("Exception thrown on Boost libraries: %s"), UTF8_TO_TCHAR(e.what()));
}
} // namespace boost
#ifndef ASIO_NO_EXCEPTIONS
# error LibCarla should be compiled with -DASIO_NO_EXCEPTIONS inside UE4.
#endif // ASIO_NO_EXCEPTIONS
#ifndef LIBCARLA_NO_EXCEPTIONS
# error LibCarla should be compiled with -DLIBCARLA_NO_EXCEPTIONS inside UE4.
#endif // LIBCARLA_NO_EXCEPTIONS
#endif // LIBCARLA_INCLUDED_DISABLE_UE4_MACROS_HEADER

View File

@ -27,6 +27,7 @@
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Notice: This file has been slightly adapted for its use by CARLA.
#pragma once
@ -156,18 +157,29 @@ namespace moodycamel { namespace details {
#define MOODYCAMEL_EXCEPTIONS_ENABLED
#endif
#endif
// ~~~ @begin Modified for CARLA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <carla/Exception.h>
#if (defined(LIBCARLA_NO_EXCEPTIONS) && defined(MOODYCAMEL_EXCEPTIONS_ENABLED))
# undef MOODYCAMEL_EXCEPTIONS_ENABLED
#endif
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
#define MOODYCAMEL_TRY try
#define MOODYCAMEL_CATCH(...) catch(__VA_ARGS__)
#define MOODYCAMEL_RETHROW throw
#define MOODYCAMEL_THROW(expr) throw (expr)
#define MOODYCAMEL_THROW(expr) ::carla::throw_exception(expr)
#else
#define MOODYCAMEL_TRY if (true)
#define MOODYCAMEL_CATCH(...) else if (false)
#define MOODYCAMEL_RETHROW
#define MOODYCAMEL_THROW(expr)
#define MOODYCAMEL_THROW(expr) ::carla::throw_exception(expr)
#endif
// ~~~ @end Modified for CARLA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifndef MOODYCAMEL_NOEXCEPT
#if !defined(MOODYCAMEL_EXCEPTIONS_ENABLED)
#define MOODYCAMEL_NOEXCEPT

View File

@ -6,7 +6,16 @@
#include "Buffer.h"
#include <boost/random/independent_bits.hpp>
/// @todo This header uses deprecated functionality, please re-enable
/// pragma-messages after upgrading Boost 1.69 if possible.
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-W#pragma-messages"
#endif
# include <boost/random/independent_bits.hpp>
#if defined(__clang__)
# pragma clang diagnostic pop
#endif
#include <climits>
#include <random>

View File

@ -6,9 +6,11 @@
#include "test.h"
#include <carla/MsgPackAdaptors.h>
#include <carla/ThreadGroup.h>
#include <carla/rpc/Actor.h>
#include <carla/rpc/Client.h>
#include <carla/rpc/Response.h>
#include <carla/rpc/Server.h>
#include <thread>
@ -57,21 +59,3 @@ TEST(rpc, server_bind_sync_run_on_game_thread) {
std::cout << "game thread: run " << i << " slices.\n";
ASSERT_TRUE(done);
}
TEST(rpc, msgpack) {
namespace c = carla;
namespace cg = carla::geom;
Actor actor;
actor.id = 42u;
actor.description.uid = 2u;
actor.description.id = "actor.random.whatever";
actor.bounding_box = cg::BoundingBox{cg::Vector3D{1.0f, 2.0f, 3.0f}};
auto buffer = c::MsgPack::Pack(actor);
auto result = c::MsgPack::UnPack<Actor>(buffer);
ASSERT_EQ(result.id, actor.id);
ASSERT_EQ(result.description.uid, actor.description.uid);
ASSERT_EQ(result.description.id, actor.description.id);
ASSERT_EQ(result.bounding_box, actor.bounding_box);
}

View File

@ -112,10 +112,13 @@ TEST(buffer, memcpy) {
ASSERT_EQ(*cpy, *msg);
}
/// @todo What to do with exceptions?
// TEST(buffer, message_too_big) {
// ASSERT_THROW(Buffer(4294967296ul), std::invalid_argument);
// }
#ifndef LIBCARLA_NO_EXCEPTIONS
TEST(buffer, message_too_big) {
ASSERT_THROW(Buffer(4294967296ul), std::invalid_argument);
Buffer buf;
ASSERT_THROW(buf.reset(4294967296ul), std::invalid_argument);
}
#endif // LIBCARLA_NO_EXCEPTIONS
TEST(buffer, buffer_pool) {
const std::string str = "Hello buffer!";

View File

@ -0,0 +1,107 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "test.h"
#include <carla/MsgPackAdaptors.h>
#include <carla/rpc/Actor.h>
#include <carla/rpc/Response.h>
#include <thread>
using namespace carla::rpc;
TEST(msgpack, response) {
using mp = carla::MsgPack;
const std::string error = "Uh ah an error!";
Response<int> r = ResponseError(error);
auto s = mp::UnPack<decltype(r)>(mp::Pack(r));
ASSERT_TRUE(r.HasError());
ASSERT_EQ(r.GetError().What(), error);
ASSERT_TRUE(s.HasError());
ASSERT_EQ(s.GetError().What(), error);
r.Reset(42);
s = mp::UnPack<decltype(r)>(mp::Pack(r));
ASSERT_FALSE(r.HasError());
ASSERT_EQ(r.Get(), 42);
ASSERT_FALSE(s.HasError());
ASSERT_EQ(s.Get(), 42);
r.SetError(error);
s = mp::UnPack<decltype(r)>(mp::Pack(r));
ASSERT_FALSE(r);
ASSERT_EQ(r.GetError().What(), error);
ASSERT_FALSE(s);
ASSERT_EQ(s.GetError().What(), error);
Response<std::vector<float>> rv;
auto sv = mp::UnPack<decltype(rv)>(mp::Pack(rv));
ASSERT_TRUE(rv.HasError());
ASSERT_TRUE(sv.HasError());
Response<void> er;
ASSERT_TRUE(er.HasError());
er = Response<void>::Success();
auto es = mp::UnPack<decltype(er)>(mp::Pack(er));
ASSERT_FALSE(er.HasError());
ASSERT_FALSE(es.HasError());
er.SetError(error);
es = mp::UnPack<decltype(er)>(mp::Pack(er));
ASSERT_FALSE(er);
ASSERT_EQ(er.GetError().What(), error);
ASSERT_FALSE(es);
ASSERT_EQ(es.GetError().What(), error);
}
TEST(msgpack, actor) {
namespace c = carla;
namespace cg = carla::geom;
Actor actor;
actor.id = 42u;
actor.description.uid = 2u;
actor.description.id = "actor.random.whatever";
actor.bounding_box = cg::BoundingBox{cg::Vector3D{1.0f, 2.0f, 3.0f}};
auto buffer = c::MsgPack::Pack(actor);
auto result = c::MsgPack::UnPack<Actor>(buffer);
ASSERT_EQ(result.id, actor.id);
ASSERT_EQ(result.description.uid, actor.description.uid);
ASSERT_EQ(result.description.id, actor.description.id);
ASSERT_EQ(result.bounding_box, actor.bounding_box);
}
TEST(msgpack, variant) {
using mp = carla::MsgPack;
boost::variant<bool, float, std::string> var;
var = true;
auto result = mp::UnPack<decltype(var)>(mp::Pack(var));
ASSERT_EQ(result.which(), 0);
ASSERT_EQ(boost::get<bool>(result), true);
var = 42.0f;
result = mp::UnPack<decltype(var)>(mp::Pack(var));
ASSERT_EQ(result.which(), 1);
ASSERT_EQ(boost::get<float>(result), 42.0f);
var = std::string("hola!");
result = mp::UnPack<decltype(var)>(mp::Pack(var));
ASSERT_EQ(result.which(), 2);
ASSERT_EQ(boost::get<std::string>(result), "hola!");
}
TEST(msgpack, optional) {
using mp = carla::MsgPack;
boost::optional<float> var;
auto result = mp::UnPack<decltype(var)>(mp::Pack(var));
ASSERT_FALSE(result.has_value());
var = 42.0f;
result = mp::UnPack<decltype(var)>(mp::Pack(var));
ASSERT_TRUE(result.has_value());
ASSERT_EQ(*result, 42.0f);
}

View File

@ -122,7 +122,7 @@ private:
};
static size_t get_max_concurrency() {
size_t concurrency = 0.75 * std::thread::hardware_concurrency();
size_t concurrency = std::thread::hardware_concurrency() / 2u;
return std::max(2ul, concurrency);
}
@ -154,9 +154,9 @@ TEST(benchmark_streaming, image_200x200_mt) {
}
TEST(benchmark_streaming, image_800x600_mt) {
benchmark_image(800u * 600u, get_max_concurrency(), 0.8);
benchmark_image(800u * 600u, get_max_concurrency(), 0.9);
}
TEST(benchmark_streaming, image_1920x1080_mt) {
benchmark_image(1920u * 1080u, get_max_concurrency(), 0.7);
benchmark_image(1920u * 1080u, get_max_concurrency(), 0.9);
}

View File

@ -0,0 +1,24 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#ifdef LIBCARLA_NO_EXCEPTIONS
#include <carla/Exception.h>
#include <carla/Logging.h>
#include <exception>
namespace carla {
void throw_exception(const std::exception &e) {
log_critical("carla::throw_exception:", e.what());
log_critical("calling std::terminate because exceptions are disabled.");
std::terminate();
}
} // namespace carla
#endif // LIBCARLA_NO_EXCEPTIONS

View File

@ -10,7 +10,7 @@
# define LIBCARLA_LOG_LEVEL LIBCARLA_LOG_LEVEL_INFO
#endif // NDEBUG
#include "test/util/Buffer.h"
#include "test/Buffer.h"
#include <carla/Logging.h>
#include <carla/profiler/Profiler.h>

View File

@ -39,7 +39,7 @@ IncludeAppLocalPrerequisites=False
bShareMaterialShaderCode=False
bSharedMaterialNativeLibraries=False
ApplocalPrerequisitesDirectory=(Path="")
IncludeCrashReporter=True
IncludeCrashReporter=False
InternationalizationPreset=English
-CulturesToStage=en
+CulturesToStage=en

View File

@ -12,7 +12,7 @@
#include "GameFramework/Controller.h"
void FActorDispatcher::Bind(FActorDefinition Definition, SpawnFunctionType Functor)
void UActorDispatcher::Bind(FActorDefinition Definition, SpawnFunctionType Functor)
{
if (UActorBlueprintFunctionLibrary::CheckActorDefinition(Definition))
{
@ -27,7 +27,7 @@ void FActorDispatcher::Bind(FActorDefinition Definition, SpawnFunctionType Funct
}
}
void FActorDispatcher::Bind(ACarlaActorFactory &ActorFactory)
void UActorDispatcher::Bind(ACarlaActorFactory &ActorFactory)
{
for (const auto &Definition : ActorFactory.GetDefinitions())
{
@ -37,7 +37,7 @@ void FActorDispatcher::Bind(ACarlaActorFactory &ActorFactory)
}
}
TPair<EActorSpawnResultStatus, FActorView> FActorDispatcher::SpawnActor(
TPair<EActorSpawnResultStatus, FActorView> UActorDispatcher::SpawnActor(
const FTransform &Transform,
FActorDescription Description)
{
@ -59,7 +59,7 @@ TPair<EActorSpawnResultStatus, FActorView> FActorDispatcher::SpawnActor(
Result.Status = EActorSpawnResultStatus::UnknownError;
}
auto View = Result.IsValid() ? Registry.Register(*Result.Actor, std::move(Description)) : FActorView();
auto View = Result.IsValid() ? RegisterActor(*Result.Actor, std::move(Description)) : FActorView();
if (!View.IsValid())
{
@ -70,7 +70,7 @@ TPair<EActorSpawnResultStatus, FActorView> FActorDispatcher::SpawnActor(
return MakeTuple(Result.Status, View);
}
bool FActorDispatcher::DestroyActor(AActor *Actor)
bool UActorDispatcher::DestroyActor(AActor *Actor)
{
if (Actor == nullptr) {
UE_LOG(LogCarla, Error, TEXT("Trying to destroy nullptr actor"));
@ -83,7 +83,7 @@ bool FActorDispatcher::DestroyActor(AActor *Actor)
UE_LOG(LogCarla, Warning, TEXT("Trying to destroy actor that is not in the registry"));
return false;
}
const auto &Id = View.GetActorDescription()->Id;
const auto &Id = View.GetActorInfo()->Description.Id;
// Destroy its controller if present.
auto Pawn = Cast<APawn>(Actor);
@ -102,9 +102,18 @@ bool FActorDispatcher::DestroyActor(AActor *Actor)
UE_LOG(LogCarla, Log, TEXT("Destroying actor: '%s'"), *Id);
if (Actor->Destroy())
{
Registry.Deregister(Actor);
return true;
}
UE_LOG(LogCarla, Error, TEXT("Failed to destroy actor: '%s'"), *Id);
return false;
}
FActorView UActorDispatcher::RegisterActor(AActor &Actor, FActorDescription Description)
{
auto View = Registry.Register(Actor, std::move(Description));
if (View.IsValid())
{
Actor.OnDestroyed.AddDynamic(this, &UActorDispatcher::OnActorDestroyed);
}
return View;
}

View File

@ -14,12 +14,17 @@
#include "Containers/Array.h"
#include "Templates/Function.h"
#include "ActorDispatcher.generated.h"
class ACarlaActorFactory;
/// Actor in charge of binding ActorDefinitions to spawn functions, as well as
/// Object in charge of binding ActorDefinitions to spawn functions, as well as
/// keeping the registry of all the actors spawned.
class FActorDispatcher
UCLASS()
class CARLA_API UActorDispatcher : public UObject
{
GENERATED_BODY()
public:
using SpawnFunctionType = TFunction<FActorSpawnResult(const FTransform &, const FActorDescription &)>;
@ -51,16 +56,15 @@ public:
/// destruction, false if indestructible or nullptr.
bool DestroyActor(AActor *Actor);
/// Register an actor that was not created using "SpawnActor" function but
/// that should be kept in the registry.
FActorView RegisterActor(AActor &Actor, FActorDescription ActorDescription);
const TArray<FActorDefinition> &GetActorDefinitions() const
{
return Definitions;
}
FActorRegistry &GetActorRegistry()
{
return Registry;
}
const FActorRegistry &GetActorRegistry() const
{
return Registry;
@ -68,6 +72,12 @@ public:
private:
UFUNCTION()
void OnActorDestroyed(AActor *Actor)
{
Registry.Deregister(Actor);
}
TArray<FActorDefinition> Definitions;
TArray<SpawnFunctionType> SpawnFunctions;

View File

@ -0,0 +1,28 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Game/Tagger.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/rpc/Actor.h>
#include <compiler/enable-ue4-macros.h>
/// A view over an actor and its properties.
struct FActorInfo
{
public:
FActorDescription Description;
TSet<ECityObjectLabel> SemanticTags;
FBoundingBox BoundingBox;
carla::rpc::Actor SerializedData;
};

View File

@ -11,30 +11,35 @@
#include "Carla/Game/Tagger.h"
#include "Carla/Traffic/TrafficLightBase.h"
#include "Carla/Util/BoundingBoxCalculator.h"
static FActorView::ActorType FActorRegistry_GetActorType(const FActorView &View)
{
if (View.IsValid())
if (!View.IsValid())
{
if (nullptr != Cast<ACarlaWheeledVehicle>(View.GetActor()))
{
return FActorView::ActorType::Vehicle;
}
else if (nullptr != Cast<ACharacter>(View.GetActor()))
{
return FActorView::ActorType::Walker;
}
else if (nullptr != Cast<ATrafficLightBase>(View.GetActor()))
{
return FActorView::ActorType::TrafficLight;
}
return FActorView::ActorType::INVALID;
}
else if (nullptr != Cast<ACarlaWheeledVehicle>(View.GetActor()))
{
return FActorView::ActorType::Vehicle;
}
else if (nullptr != Cast<ACharacter>(View.GetActor()))
{
return FActorView::ActorType::Walker;
}
else if (nullptr != Cast<ATrafficLightBase>(View.GetActor()))
{
return FActorView::ActorType::TrafficLight;
}
else
{
return FActorView::ActorType::Other;
}
return FActorView::ActorType::Other;
}
static FString GetRelevantTagAsString(const FActorView &View)
static FString GetRelevantTagAsString(const TSet<ECityObjectLabel> &SemanticTags)
{
for (auto &&Tag : View.GetSemanticTags())
for (auto &&Tag : SemanticTags)
{
if ((Tag != ECityObjectLabel::None) && (Tag != ECityObjectLabel::Other))
{
@ -61,9 +66,7 @@ FActorView FActorRegistry::Register(AActor &Actor, FActorDescription Description
}
Ids.Emplace(&Actor, Id);
auto View = FActorView(Id, Actor, std::move(Description));
ATagger::GetTagsOfTaggedActor(Actor, View.SemanticTags);
View.Type = FActorRegistry_GetActorType(View);
auto View = MakeView(Id, Actor, std::move(Description));
auto Result = ActorDatabase.emplace(Id, View);
check(Result.second);
@ -74,7 +77,7 @@ FActorView FActorRegistry::Register(AActor &Actor, FActorDescription Description
void FActorRegistry::Deregister(IdType Id)
{
check(Contains(Id));
AActor *Actor = FindActor(Id);
AActor *Actor = Find(Id).GetActor();
check(Actor != nullptr);
ActorDatabase.erase(Id);
Actors.Remove(Id);
@ -84,26 +87,53 @@ void FActorRegistry::Deregister(IdType Id)
void FActorRegistry::Deregister(AActor *Actor)
{
check(Actor != nullptr);
auto View = Find(Actor);
check(View.IsValid());
check(View.GetActor() == Actor);
Deregister(View.GetActorId());
}
FActorView FActorRegistry::FindOrFake(AActor *Actor) const
{
if (Actor == nullptr)
{
return {};
}
auto View = Find(Actor);
if (!View.IsValid())
const bool bFakeActor = (View.GetActor() == nullptr) && (Actor != nullptr);
return bFakeActor ? MakeView(0u, *Actor, FActorDescription{}) : View;
}
FActorView FActorRegistry::MakeView(
IdType Id,
AActor &Actor,
FActorDescription Description) const
{
auto Info = MakeShared<FActorInfo>();
Info->Description = std::move(Description);
ATagger::GetTagsOfTaggedActor(Actor, Info->SemanticTags);
Info->BoundingBox = UBoundingBoxCalculator::GetActorBoundingBox(&Actor);
if (Info->Description.Id.IsEmpty())
{
View.TheActor = Actor;
ATagger::GetTagsOfTaggedActor(*Actor, View.SemanticTags);
auto Description = MakeShared<FActorDescription>();
Description->Id = TEXT("static.") + GetRelevantTagAsString(View);
View.Description = Description;
check(View.IsValid());
// This is a fake actor, let's fake the id based on their semantic tags.
Info->Description.Id = TEXT("static.") + GetRelevantTagAsString(Info->SemanticTags);
}
Info->SerializedData.id = Id;
Info->SerializedData.description = Info->Description;
Info->SerializedData.bounding_box = Info->BoundingBox;
Info->SerializedData.semantic_tags.reserve(Info->SemanticTags.Num());
for (auto &&Tag : Info->SemanticTags)
{
using tag_t = decltype(Info->SerializedData.semantic_tags)::value_type;
Info->SerializedData.semantic_tags.emplace_back(static_cast<tag_t>(Tag));
}
auto *Sensor = Cast<ASensor>(&Actor);
if (Sensor != nullptr)
{
const auto &Token = Sensor->GetToken();
Info->SerializedData.stream_token = decltype(Info->SerializedData.stream_token)(
std::begin(Token.data),
std::end(Token.data));
}
auto View = FActorView{Id, Actor, std::move(Info)};
View.Type = FActorRegistry_GetActorType(View);
return View;
}

View File

@ -10,6 +10,10 @@
#include "Containers/Map.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/Iterator.h>
#include <compiler/enable-ue4-macros.h>
#include <unordered_map>
/// A registry of all the Carla actors.
@ -72,12 +76,6 @@ public:
return PtrToId != nullptr ? Find(*PtrToId) : FActorView();
}
AActor *FindActor(IdType Id) const
{
auto View = Find(Id);
return View.IsValid() ? View.GetActor() : nullptr;
}
/// If the actor is not found in the registry, create a fake actor view. The
/// returned FActorView has some information about the @a Actor but will have
/// an invalid id.
@ -90,24 +88,23 @@ public:
/// @{
public:
using key_type = DatabaseType::key_type;
using mapped_type = DatabaseType::mapped_type;
using value_type = DatabaseType::value_type;
using const_iterator = DatabaseType::const_iterator;
using value_type = DatabaseType::mapped_type;
const_iterator begin() const noexcept
auto begin() const noexcept
{
return ActorDatabase.begin();
return carla::iterator::make_map_values_iterator(ActorDatabase.begin());
}
const_iterator end() const noexcept
auto end() const noexcept
{
return ActorDatabase.end();
return carla::iterator::make_map_values_iterator(ActorDatabase.end());
}
/// @}
private:
FActorView MakeView(IdType Id, AActor &Actor, FActorDescription Description) const;
TMap<IdType, AActor *> Actors;
TMap<AActor *, IdType> Ids;

View File

@ -6,8 +6,7 @@
#pragma once
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Game/Tagger.h"
#include "Carla/Actor/ActorInfo.h"
class AActor;
@ -22,15 +21,17 @@ public:
Other,
Vehicle,
Walker,
TrafficLight
TrafficLight,
INVALID
};
FActorView() = default;
FActorView(const FActorView &) = default;
FActorView(FActorView &&) = default;
bool IsValid() const
{
return (TheActor != nullptr) && Description.IsValid();
return (TheActor != nullptr) && !TheActor->IsPendingKill();
}
IdType GetActorId() const
@ -53,24 +54,19 @@ public:
return TheActor;
}
const FActorDescription *GetActorDescription() const
const FActorInfo *GetActorInfo() const
{
return Description.Get();
}
const TSet<ECityObjectLabel> &GetSemanticTags() const
{
return SemanticTags;
return Info.Get();
}
private:
friend class FActorRegistry;
FActorView(IdType ActorId, AActor &Actor, FActorDescription Description)
FActorView(IdType ActorId, AActor &Actor, TSharedPtr<const FActorInfo> Info)
: Id(ActorId),
TheActor(&Actor),
Description(MakeShared<FActorDescription>(std::move(Description))) {}
Info(std::move(Info)) {}
IdType Id = 0u;
@ -78,7 +74,5 @@ private:
AActor *TheActor = nullptr;
TSharedPtr<const FActorDescription> Description = nullptr;
TSet<ECityObjectLabel> SemanticTags;
TSharedPtr<const FActorInfo> Info = nullptr;
};

View File

@ -135,7 +135,9 @@ public class Carla : ModuleRules
PublicIncludePaths.Add(LibCarlaIncludePath);
PrivateIncludePaths.Add(LibCarlaIncludePath);
/// @todo This is necessary because rpclib uses exceptions to notify errors.
bEnableExceptions = true;
PublicDefinitions.Add("ASIO_NO_EXCEPTIONS");
PublicDefinitions.Add("BOOST_NO_EXCEPTIONS");
PublicDefinitions.Add("LIBCARLA_NO_EXCEPTIONS");
PublicDefinitions.Add("PUGIXML_NO_EXCEPTIONS");
}
}

View File

@ -29,23 +29,23 @@ void FCarlaModule::RegisterSettings()
// Registering some settings is just a matter of exposing the default UObject of
// your desired class, add here all those settings you want to expose
// to your LDs or artists.
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
// Create the new category
ISettingsContainerPtr SettingsContainer = SettingsModule->GetContainer("Project");
SettingsContainer->DescribeCategory("CARLASettings",
LOCTEXT("RuntimeWDCategoryName", "CARLA Settings"),
LOCTEXT("RuntimeWDCategoryDescription", "CARLA plugin settings"));
// Register the settings
ISettingsSectionPtr SettingsSection = SettingsModule->RegisterSettings("Project", "CARLASettings", "General",
LOCTEXT("RuntimeGeneralSettingsName", "General"),
LOCTEXT("RuntimeGeneralSettingsDescription", "General configuration for the CARLA plugin"),
GetMutableDefault<UCarlaSettings>()
);
// Register the save handler to your settings, you might want to use it to
// validate those or just act to settings changes.
if (SettingsSection.IsValid())
@ -54,12 +54,12 @@ void FCarlaModule::RegisterSettings()
}
}
}
void FCarlaModule::UnregisterSettings()
{
// Ensure to unregister all of your registered settings here, hot-reload would
// otherwise yield unexpected results.
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
SettingsModule->UnregisterSettings("Project", "CustomSettings", "General");
@ -70,18 +70,38 @@ bool FCarlaModule::HandleSettingsSaved()
{
UCarlaSettings* Settings = GetMutableDefault<UCarlaSettings>();
bool ResaveSettings = false;
// Put any validation code in here and resave the settings in case an invalid
// value has been entered
if (ResaveSettings)
{
Settings->SaveConfig();
}
return true;
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FCarlaModule, Carla)
// =============================================================================
// -- Implement carla throw_exception ------------------------------------------
// =============================================================================
#include <compiler/disable-ue4-macros.h>
#include <carla/Exception.h>
#include <compiler/enable-ue4-macros.h>
#include <exception>
namespace carla {
void throw_exception(const std::exception &e) {
UE_LOG(LogCarla, Fatal, TEXT("Exception thrown: %s"), UTF8_TO_TCHAR(e.what()));
// It should never reach this part.
std::terminate();
}
} // namespace carla

View File

@ -7,6 +7,8 @@
#include "Carla.h"
#include "Carla/Game/CarlaEpisode.h"
#include "Carla/Sensor/Sensor.h"
#include "Carla/Util/BoundingBoxCalculator.h"
#include "Carla/Vehicle/VehicleSpawnPoint.h"
#include "EngineUtils.h"
@ -36,9 +38,11 @@ UCarlaEpisode::UCarlaEpisode(const FObjectInitializer &ObjectInitializer)
Id([]() {
static uint32 COUNTER = 0u;
return ++COUNTER;
}()) {}
}()) {
ActorDispatcher = CreateDefaultSubobject<UActorDispatcher>(TEXT("ActorDispatcher"));
}
TArray<FTransform> UCarlaEpisode::GetRecommendedStartTransforms() const
TArray<FTransform> UCarlaEpisode::GetRecommendedSpawnPoints() const
{
TArray<FTransform> SpawnPoints;
for (TActorIterator<AVehicleSpawnPoint> It(GetWorld()); It; ++It) {
@ -47,22 +51,29 @@ TArray<FTransform> UCarlaEpisode::GetRecommendedStartTransforms() const
return SpawnPoints;
}
const AWorldObserver *UCarlaEpisode::StartWorldObserver(carla::streaming::MultiStream Stream)
carla::rpc::Actor UCarlaEpisode::SerializeActor(FActorView ActorView) const
{
UE_LOG(LogCarla, Log, TEXT("Starting AWorldObserver sensor"));
check(WorldObserver == nullptr);
auto *World = GetWorld();
check(World != nullptr);
WorldObserver = World->SpawnActorDeferred<AWorldObserver>(
AWorldObserver::StaticClass(),
FTransform(),
nullptr,
nullptr,
ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
WorldObserver->SetEpisode(*this);
WorldObserver->SetStream(std::move(Stream));
UGameplayStatics::FinishSpawningActor(WorldObserver, FTransform());
return WorldObserver;
carla::rpc::Actor Actor;
if (ActorView.IsValid())
{
Actor = ActorView.GetActorInfo()->SerializedData;
auto Parent = ActorView.GetActor()->GetOwner();
if (Parent != nullptr)
{
Actor.parent_id = FindActor(Parent).GetActorId();
}
} else {
UE_LOG(LogCarla, Warning, TEXT("Trying to serialize invalid actor"));
}
return Actor;
}
void UCarlaEpisode::AttachActors(AActor *Child, AActor *Parent)
{
check(Child != nullptr);
check(Parent != nullptr);
Child->AttachToActor(Parent, FAttachmentTransformRules::KeepRelativeTransform);
Child->SetOwner(Parent);
}
void UCarlaEpisode::InitializeAtBeginPlay()
@ -81,7 +92,7 @@ void UCarlaEpisode::InitializeAtBeginPlay()
FActorDescription Description;
Description.Id = TEXT("spectator");
Description.Class = Spectator->GetClass();
ActorDispatcher.GetActorRegistry().Register(*Spectator, Description);
ActorDispatcher->RegisterActor(*Spectator, Description);
}
else
{
@ -95,6 +106,6 @@ void UCarlaEpisode::InitializeAtBeginPlay()
FActorDescription Description;
Description.Id = UCarlaEpisode_GetTrafficSignId(Actor->GetTrafficSignState());
Description.Class = Actor->GetClass();
ActorDispatcher.GetActorRegistry().Register(*Actor, Description);
ActorDispatcher->RegisterActor(*Actor, Description);
}
}

View File

@ -10,6 +10,12 @@
#include "Carla/Sensor/WorldObserver.h"
#include "Carla/Weather/Weather.h"
#include "GameFramework/Pawn.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/rpc/Actor.h>
#include <compiler/enable-ue4-macros.h>
#include "CarlaEpisode.generated.h"
/// A simulation episode.
@ -20,21 +26,51 @@ class CARLA_API UCarlaEpisode : public UObject
{
GENERATED_BODY()
// ===========================================================================
// -- Constructor ------------------------------------------------------------
// ===========================================================================
public:
UCarlaEpisode(const FObjectInitializer &ObjectInitializer);
// ===========================================================================
// -- Retrieve info about this episode ---------------------------------------
// ===========================================================================
public:
/// Return the unique id of this episode.
auto GetId() const
{
return Id;
}
/// Return the name of the map loaded in this episode.
UFUNCTION(BlueprintCallable)
const FString &GetMapName() const
{
return MapName;
}
/// Return the list of actor definitions that are available to be spawned this
/// episode.
UFUNCTION(BlueprintCallable)
const TArray<FActorDefinition> &GetActorDefinitions() const
{
return ActorDispatcher->GetActorDefinitions();
}
/// Return the list of recommended spawn points for vehicles.
UFUNCTION(BlueprintCallable)
TArray<FTransform> GetRecommendedSpawnPoints() const;
// ===========================================================================
// -- Retrieve special actors ------------------------------------------------
// ===========================================================================
public:
UFUNCTION(BlueprintCallable)
APawn *GetSpectatorPawn() const
{
@ -47,17 +83,55 @@ public:
return Weather;
}
/// Return the list of actor definitions that are available to be spawned this
/// episode.
UFUNCTION(BlueprintCallable)
const TArray<FActorDefinition> &GetActorDefinitions() const
const AWorldObserver *GetWorldObserver() const
{
return ActorDispatcher.GetActorDefinitions();
return WorldObserver;
}
/// Return the list of recommended start positions.
UFUNCTION(BlueprintCallable)
TArray<FTransform> GetRecommendedStartTransforms() const;
const FActorRegistry &GetActorRegistry() const
{
return ActorDispatcher->GetActorRegistry();
}
// ===========================================================================
// -- Actor look up methods --------------------------------------------------
// ===========================================================================
public:
/// Find a Carla actor by id.
///
/// If the actor is not found or is pending kill, the returned view is
/// invalid.
FActorView FindActor(FActorView::IdType ActorId) const
{
return ActorDispatcher->GetActorRegistry().Find(ActorId);
}
/// Find the actor view of @a Actor.
///
/// If the actor is not found or is pending kill, the returned view is
/// invalid.
FActorView FindActor(AActor *Actor) const
{
return ActorDispatcher->GetActorRegistry().Find(Actor);
}
/// Find the actor view of @a Actor. If the actor is not found, a "fake" view
/// is returned emulating an existing Carla actor. Use this to return views
/// over static actors present in the map.
///
/// If the actor is pending kill, the returned view is invalid.
FActorView FindOrFakeActor(AActor *Actor) const
{
return ActorDispatcher->GetActorRegistry().FindOrFake(Actor);
}
// ===========================================================================
// -- Actor handling methods -------------------------------------------------
// ===========================================================================
public:
/// Spawns an actor based on @a ActorDescription at @a Transform. To properly
/// despawn an actor created with this function call DestroyActor.
@ -69,7 +143,7 @@ public:
const FTransform &Transform,
FActorDescription ActorDescription)
{
return ActorDispatcher.SpawnActor(Transform, std::move(ActorDescription));
return ActorDispatcher->SpawnActor(Transform, std::move(ActorDescription));
}
/// Spawns an actor based on @a ActorDescription at @a Transform. To properly
@ -86,24 +160,31 @@ public:
return SpawnActorWithInfo(Transform, std::move(ActorDescription)).Value.GetActor();
}
/// Attach @a Child to @a Parent.
///
/// @pre Actors cannot be null.
UFUNCTION(BlueprintCallable)
void AttachActors(AActor *Child, AActor *Parent);
/// @copydoc FActorDispatcher::DestroyActor(AActor*)
UFUNCTION(BlueprintCallable)
bool DestroyActor(AActor *Actor)
{
return ActorDispatcher.DestroyActor(Actor);
return ActorDispatcher->DestroyActor(Actor);
}
const FActorRegistry &GetActorRegistry() const
{
return ActorDispatcher.GetActorRegistry();
}
// ===========================================================================
// -- Other methods ----------------------------------------------------------
// ===========================================================================
const AWorldObserver *StartWorldObserver(carla::streaming::MultiStream Stream);
public:
const AWorldObserver *GetWorldObserver() const
{
return WorldObserver;
}
/// Create a serializable object describing the actor.
carla::rpc::Actor SerializeActor(FActorView ActorView) const;
// ===========================================================================
// -- Private methods and members --------------------------------------------
// ===========================================================================
private:
@ -113,7 +194,7 @@ private:
void RegisterActorFactory(ACarlaActorFactory &ActorFactory)
{
ActorDispatcher.Bind(ActorFactory);
ActorDispatcher->Bind(ActorFactory);
}
const uint32 Id = 0u;
@ -121,7 +202,8 @@ private:
UPROPERTY(VisibleAnywhere)
FString MapName;
FActorDispatcher ActorDispatcher;
UPROPERTY(VisibleAnywhere)
UActorDispatcher *ActorDispatcher = nullptr;
UPROPERTY(VisibleAnywhere)
APawn *Spectator = nullptr;

View File

@ -25,7 +25,7 @@ UCarlaGameInstance::UCarlaGameInstance() {
CarlaSettings->LogSettings();
}
UCarlaGameInstance::~UCarlaGameInstance() {}
UCarlaGameInstance::~UCarlaGameInstance() = default;
void UCarlaGameInstance::InitializeGameControllerIfNotPresent(
const FMockGameControllerSettings &MockControllerSettings)
@ -40,7 +40,7 @@ void UCarlaGameInstance::InitializeGameControllerIfNotPresent(
}
}
void UCarlaGameInstance::NotifyBeginEpisode(UCarlaEpisode &Episode)
void UCarlaGameInstance::StartServer()
{
if (!bServerIsRunning)
{
@ -48,10 +48,4 @@ void UCarlaGameInstance::NotifyBeginEpisode(UCarlaEpisode &Episode)
Server.AsyncRun(GetNumberOfThreadsForRPCServer());
bServerIsRunning = true;
}
Server.NotifyBeginEpisode(Episode);
}
void UCarlaGameInstance::NotifyEndEpisode()
{
Server.NotifyEndEpisode();
}

View File

@ -33,6 +33,9 @@ public:
void InitializeGameControllerIfNotPresent(
const FMockGameControllerSettings &MockControllerSettings);
/// Starts the Carla server if not already running.
void StartServer();
ICarlaGameControllerBase &GetGameController()
{
check(GameController != nullptr);
@ -58,19 +61,33 @@ public:
return CarlaSettings;
}
UFUNCTION(BlueprintCallable)
UCarlaEpisode *GetCarlaEpisode()
{
return CurrentEpisode;
}
FDataRouter &GetDataRouter()
{
return DataRouter;
}
void NotifyBeginEpisode(UCarlaEpisode &Episode);
void NotifyBeginEpisode(UCarlaEpisode &Episode)
{
CurrentEpisode = &Episode;
Server.NotifyBeginEpisode(Episode);
}
void Tick(float /*DeltaSeconds*/)
{
Server.RunSome(10u); /// @todo
}
void NotifyEndEpisode();
void NotifyEndEpisode()
{
Server.NotifyEndEpisode();
CurrentEpisode = nullptr;
}
const FTheNewCarlaServer &GetServer() const
{
@ -85,6 +102,9 @@ private:
UPROPERTY(Category = "CARLA Settings", EditAnywhere)
UCarlaSettings *CarlaSettings = nullptr;
UPROPERTY()
UCarlaEpisode *CurrentEpisode = nullptr;
FDataRouter DataRouter;
TUniquePtr<ICarlaGameControllerBase> GameController;

View File

@ -0,0 +1,68 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "Carla/Game/CarlaEpisode.h"
#include "Carla/Game/CarlaGameInstance.h"
#include "Carla/Game/TheNewCarlaGameModeBase.h"
#include "Carla/Settings/CarlaSettings.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CarlaStatics.generated.h"
// =============================================================================
// -- UCarlaStatics declaration ------------------------------------------------
// =============================================================================
UCLASS()
class CARLA_API UCarlaStatics : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, Category="CARLA", meta=(WorldContext="WorldContextObject"))
static ATheNewCarlaGameModeBase *GetGameMode(const UObject *WorldContextObject);
UFUNCTION(BlueprintPure, Category="CARLA", meta=(WorldContext="WorldContextObject"))
static UCarlaGameInstance *GetGameInstance(const UObject *WorldContextObject);
UFUNCTION(BlueprintPure, Category="CARLA", meta=(WorldContext="WorldContextObject"))
static UCarlaEpisode *GetCurrentEpisode(const UObject *WorldContextObject);
UFUNCTION(BlueprintPure, Category="CARLA", meta=(WorldContext="WorldContextObject"))
static UCarlaSettings *GetCarlaSettings(const UObject *WorldContextObject);
};
// =============================================================================
// -- UCarlaStatics implementation ---------------------------------------------
// =============================================================================
inline ATheNewCarlaGameModeBase *UCarlaStatics::GetGameMode(const UObject *WorldContext)
{
return Cast<ATheNewCarlaGameModeBase>(UGameplayStatics::GetGameMode(WorldContext));
}
inline UCarlaGameInstance *UCarlaStatics::GetGameInstance(const UObject *WorldContext)
{
return Cast<UCarlaGameInstance>(UGameplayStatics::GetGameInstance(WorldContext));
}
inline UCarlaEpisode *UCarlaStatics::GetCurrentEpisode(const UObject *WorldContext)
{
auto GameInstance = GetGameInstance(WorldContext);
return GameInstance != nullptr ? GameInstance->GetCarlaEpisode() : nullptr;
}
inline UCarlaSettings *UCarlaStatics::GetCarlaSettings(const UObject *WorldContext)
{
auto GameInstance = GetGameInstance(WorldContext);
return GameInstance != nullptr ? GameInstance->GetCARLASettings() : nullptr;
}

View File

@ -75,6 +75,12 @@ void ATheNewCarlaGameModeBase::InitGame(
UE_LOG(LogCarla, Error, TEXT("Missing weather class!"));
}
GameInstance->StartServer();
Episode->WorldObserver = World->SpawnActor<AWorldObserver>();
Episode->WorldObserver->SetEpisode(*Episode);
Episode->WorldObserver->SetStream(GameInstance->GetServer().OpenMultiStream());
SpawnActorFactories();
}

View File

@ -0,0 +1,107 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include <compiler/disable-ue4-macros.h>
#include <carla/Buffer.h>
#include <carla/sensor/SensorRegistry.h>
#include <carla/sensor/s11n/SensorHeaderSerializer.h>
#include <carla/streaming/Stream.h>
#include <compiler/enable-ue4-macros.h>
template <typename T>
class FDataStreamTmpl;
// =============================================================================
// -- FAsyncDataStreamTmpl -----------------------------------------------------
// =============================================================================
/// A streaming channel for sending sensor data to clients, supports sending
/// data asynchronously. Data sent by the "Send" functions is passed to the
/// serializer registered with the sensor at carla::sensor:SensorRegistry before
/// being sent down the stream.
///
/// @warning This is a single-use object, a new one needs to be created for each
/// new message.
///
/// FAsyncDataStream also has a pool of carla::Buffer that allows reusing the
/// allocated memory, use it whenever possible.
template <typename T>
class FAsyncDataStreamTmpl
{
public:
using StreamType = T;
FAsyncDataStreamTmpl(FAsyncDataStreamTmpl &&) = default;
/// Return the token that allows subscribing to this stream.
auto GetToken() const
{
return Stream.GetToken();
}
/// Pop a Buffer from the pool. Buffers in the pool can reuse the memory
/// allocated by previous messages, significantly improving performance for
/// big messages.
carla::Buffer PopBufferFromPool()
{
return Stream.MakeBuffer();
}
/// Send some data down the stream.
template <typename SensorT, typename... ArgsT>
void Send(SensorT &Sensor, ArgsT &&... Args);
private:
friend class FDataStreamTmpl<T>;
/// @pre This functions needs to be called in the game-thread.
template <typename SensorT>
explicit FAsyncDataStreamTmpl(const SensorT &InSensor, StreamType InStream);
StreamType Stream;
carla::Buffer Header;
};
// =============================================================================
// -- FAsyncDataStream and FAsyncDataMultiStream -------------------------------
// =============================================================================
using FAsyncDataStream = FAsyncDataStreamTmpl<carla::streaming::Stream>;
using FAsyncDataMultiStream = FAsyncDataStreamTmpl<carla::streaming::MultiStream>;
// =============================================================================
// -- FAsyncDataStreamTmpl implementation --------------------------------------
// =============================================================================
template <typename T>
template <typename SensorT, typename... ArgsT>
inline void FAsyncDataStreamTmpl<T>::Send(SensorT &Sensor, ArgsT &&... Args)
{
Stream.Write(
std::move(Header),
carla::sensor::SensorRegistry::Serialize(Sensor, std::forward<ArgsT>(Args)...));
}
template <typename T>
template <typename SensorT>
inline FAsyncDataStreamTmpl<T>::FAsyncDataStreamTmpl(
const SensorT &Sensor,
StreamType InStream)
: Stream(std::move(InStream)),
Header([&Sensor]() {
check(IsInGameThread());
using Serializer = carla::sensor::s11n::SensorHeaderSerializer;
return Serializer::Serialize(
carla::sensor::SensorRegistry::template get<SensorT*>::index,
GFrameCounter,
Sensor.GetActorTransform());
}()) {}

View File

@ -50,13 +50,6 @@ void ACollisionSensor::BeginPlay()
return;
}
Episode = &GameMode->GetCarlaEpisode();
GameInstance = Cast<UCarlaGameInstance>(GetGameInstance());
if (GameMode == nullptr)
{
UE_LOG(LogCarla, Error, TEXT("ACollisionSensor: Game instance not compatible with this sensor"));
return;
}
}
void ACollisionSensor::OnCollisionEvent(
@ -65,16 +58,14 @@ void ACollisionSensor::OnCollisionEvent(
FVector NormalImpulse,
const FHitResult &Hit)
{
if ((Episode != nullptr) && (GameInstance != nullptr) && (Actor != nullptr) && (OtherActor != nullptr))
if ((Episode != nullptr) && (Actor != nullptr) && (OtherActor != nullptr))
{
const auto &Registry = Episode->GetActorRegistry();
const auto &Server = GameInstance->GetServer();
constexpr float TO_METERS = 1e-2;
NormalImpulse *= TO_METERS;
GetDataStream().Send_GameThread(
GetDataStream(*this).Send(
*this,
Server.SerializeActor(Registry.FindOrFake(Actor)),
Server.SerializeActor(Registry.FindOrFake(OtherActor)),
Episode->SerializeActor(Episode->FindOrFakeActor(Actor)),
Episode->SerializeActor(Episode->FindOrFakeActor(OtherActor)),
carla::geom::Vector3D{NormalImpulse.X, NormalImpulse.Y, NormalImpulse.Z});
}
}

View File

@ -6,6 +6,7 @@
#pragma once
#include "Carla/Actor/ActorDefinition.h"
#include "Carla/Sensor/Sensor.h"
#include "CollisionSensor.generated.h"
@ -40,7 +41,4 @@ private:
UPROPERTY()
const UCarlaEpisode *Episode = nullptr;
UPROPERTY()
const UCarlaGameInstance *GameInstance = nullptr;
};

View File

@ -6,47 +6,22 @@
#pragma once
#include "Carla/Sensor/AsyncDataStream.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/Buffer.h>
#include <carla/Optional.h>
#include <carla/sensor/SensorRegistry.h>
#include <carla/sensor/s11n/SensorHeaderSerializer.h>
#include <carla/streaming/Stream.h>
#include <boost/optional.hpp>
#include <compiler/enable-ue4-macros.h>
// =============================================================================
// -- FSensorMessageHeader -----------------------------------------------------
// =============================================================================
/// Contains meta-information of a sensor message.
class FSensorMessageHeader
{
public:
FSensorMessageHeader(FSensorMessageHeader &&) = default;
private:
FSensorMessageHeader(carla::Buffer InBuffer) : Buffer(std::move(InBuffer)) {}
template <typename T>
friend class FDataStreamTmpl;
carla::Buffer Buffer;
};
// =============================================================================
// -- FDataStreamTmpl ----------------------------------------------------------
// =============================================================================
/// A streaming channel for sending sensor data to clients. Each sensor has its
/// own FDataStream. Use Send_GameThread and Send_Async for sending data
/// generated by the sensor. Data sent by these functions is passed to the
/// serializer registered with the sensor at carla::sensor:SensorRegistry before
/// being sent down the stream.
///
/// FDataStream also has a pool of carla::Buffer that allows reusing the
/// allocated memory, use it whenever possible.
/// own FDataStream. Note however that this class does not provide a send
/// function. In order to send data, a FAsyncDataStream needs to be created
/// using "MakeAsyncDataStream" function. FAsyncDataStream allows sending data
/// from any thread.
template <typename T>
class FDataStreamTmpl
{
@ -56,37 +31,28 @@ public:
FDataStreamTmpl() = default;
FDataStreamTmpl(FDataStreamTmpl &&) = default;
FDataStreamTmpl &operator=(FDataStreamTmpl &&) = default;
FDataStreamTmpl(StreamType InStream) : Stream(std::move(InStream)) {}
/// Create the meta-information header associated with the sensor message.
/// This functions needs to be called in the game-thread.
/// Create a FAsyncDataStream object.
///
/// @pre This functions needs to be called in the game-thread.
template <typename SensorT>
FSensorMessageHeader MakeHeader(const SensorT &Sensor);
auto MakeAsyncDataStream(const SensorT &Sensor)
{
check(Stream.has_value());
return FAsyncDataStreamTmpl<T>{Sensor, *Stream};
}
/// Pop a Buffer from the pool. Buffers in the pool can reuse the memory
/// allocated by previous messages, significantly improving performance for
/// big messages.
carla::Buffer PopBufferFromPool();
/// Send some data down the stream. This function can only be called from the
/// game-thread. No need to provide a FSensorMessageHeader to this function.
template <typename SensorT, typename... ArgsT>
void Send_GameThread(SensorT &Sensor, ArgsT &&... Args);
/// Send some data down the stream. This function can be called from a
/// different thread. It requires you however to provide a
/// FSensorMessageHeader previously generated in the game-thread.
template <typename SensorT, typename... ArgsT>
void Send_Async(FSensorMessageHeader Header, SensorT &Sensor, ArgsT &&... Args);
auto GetToken() const;
/// Return the token that allows subscribing to this stream.
auto GetToken() const
{
check(Stream.has_value());
return (*Stream).token();
}
private:
carla::Optional<StreamType> Stream;
boost::optional<StreamType> Stream;
};
// =============================================================================
@ -96,64 +62,3 @@ private:
using FDataStream = FDataStreamTmpl<carla::streaming::Stream>;
using FDataMultiStream = FDataStreamTmpl<carla::streaming::MultiStream>;
// =============================================================================
// -- FDataStreamTmpl implementation -------------------------------------------
// =============================================================================
template <typename T>
template <typename SensorT>
inline FSensorMessageHeader FDataStreamTmpl<T>::MakeHeader(const SensorT &Sensor)
{
check(IsInGameThread());
using Serializer = carla::sensor::s11n::SensorHeaderSerializer;
return {Serializer::Serialize(
carla::sensor::SensorRegistry::template get<SensorT*>::index,
GFrameCounter,
Sensor.GetActorTransform())};
}
template <typename T>
inline carla::Buffer FDataStreamTmpl<T>::PopBufferFromPool()
{
#ifdef WITH_EDITOR
if (!Stream.has_value())
{
UE_LOG(LogCarla, Error, TEXT("Sensor does not have a stream!"));
return {};
}
#endif // WITH_EDITOR
check(Stream.has_value());
return (*Stream).MakeBuffer();
}
template <typename T>
template <typename SensorT, typename... ArgsT>
inline void FDataStreamTmpl<T>::Send_Async(FSensorMessageHeader Header, SensorT &Sensor, ArgsT &&... Args)
{
#ifdef WITH_EDITOR
if (!Stream.has_value())
{
UE_LOG(LogCarla, Error, TEXT("Sensor does not have a stream!"));
return;
}
#endif // WITH_EDITOR
check(Stream.has_value());
(*Stream).Write(
std::move(Header.Buffer),
carla::sensor::SensorRegistry::Serialize(Sensor, std::forward<ArgsT>(Args)...));
}
template <typename T>
template <typename SensorT, typename... ArgsT>
inline void FDataStreamTmpl<T>::Send_GameThread(SensorT &Sensor, ArgsT &&... Args)
{
Send_Async(MakeHeader(Sensor), Sensor, std::forward<ArgsT>(Args)...);
}
template <typename T>
inline auto FDataStreamTmpl<T>::GetToken() const
{
check(Stream.has_value());
return (*Stream).token();
}

View File

@ -87,26 +87,31 @@ void FPixelReader::SendPixelsInRenderThread(TSensor &Sensor)
// First we create the message header (needs to be created in the
// game-thread).
auto Header = Sensor.GetDataStream().MakeHeader(Sensor);
auto AsyncStream = Sensor.GetDataStream(Sensor);
// We need a shared ptr here because UE4 macros do not move the arguments -_-
auto HeaderPtr = MakeShared<decltype(Header)>(std::move(Header));
auto StreamPtr = std::make_shared<decltype(AsyncStream)>(std::move(AsyncStream));
// Then we enqueue commands in the render-thread that will write the image
// buffer to the data stream.
auto WriteAndSend = [&Sensor, Hdr=std::move(HeaderPtr)](auto &InRHICmdList) mutable {
auto &Stream = Sensor.GetDataStream();
auto Buffer = Stream.PopBufferFromPool();
WritePixelsToBuffer(
*Sensor.CaptureRenderTarget,
Buffer,
carla::sensor::SensorRegistry::get<TSensor *>::type::header_offset,
InRHICmdList);
Stream.Send_Async(std::move(*Hdr), Sensor, std::move(Buffer));
auto WriteAndSend = [&Sensor, Stream=std::move(StreamPtr)](auto &InRHICmdList) mutable
{
/// @todo Can we make sure the sensor is not going to be destroyed?
if (!Sensor.IsPendingKill())
{
auto Buffer = Stream->PopBufferFromPool();
WritePixelsToBuffer(
*Sensor.CaptureRenderTarget,
Buffer,
carla::sensor::SensorRegistry::get<TSensor *>::type::header_offset,
InRHICmdList);
Stream->Send(Sensor, std::move(Buffer));
}
};
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
FWritePixels_Vulkan,
FWritePixels_SendPixelsInRenderThread,
std::function<void(FRHICommandListImmediate &)>, WriteAndSendFunction, std::move(WriteAndSend),
{
WriteAndSendFunction(RHICmdList);

View File

@ -69,8 +69,8 @@ void ARayCastLidar::Tick(const float DeltaTime)
ReadPoints(DeltaTime);
auto &DataStream = GetDataStream();
DataStream.Send_GameThread(*this, LidarMeasurement, DataStream.PopBufferFromPool());
auto DataStream = GetDataStream(*this);
DataStream.Send(*this, LidarMeasurement, DataStream.PopBufferFromPool());
}
void ARayCastLidar::ReadPoints(const float DeltaTime)

View File

@ -6,9 +6,11 @@
#pragma once
#include "Carla/Sensor/PixelReader.h"
#include "Carla/Sensor/SceneCaptureSensor.h"
#include "Carla/Actor/ActorDefinition.h"
#include "Carla/Sensor/PixelReader.h"
#include "SceneCaptureCamera.generated.h"
/// A sensor that captures images from the scene.

View File

@ -7,6 +7,8 @@
#include "Carla.h"
#include "Carla/Sensor/SceneCaptureSensor.h"
#include "Carla/Game/CarlaStatics.h"
#include "Components/DrawFrustumComponent.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Components/StaticMeshComponent.h"
@ -28,10 +30,9 @@ namespace SceneCaptureSensor_local_ns {
static auto GetQualitySettings(UWorld *World)
{
check(World != nullptr);
const auto *GameInstance = Cast<UCarlaGameInstance>(World->GetGameInstance());
check(GameInstance != nullptr);
return GameInstance->GetCarlaSettings().GetQualityLevel();
auto Settings = UCarlaStatics::GetCarlaSettings(World);
check(Settings != nullptr);
return Settings->GetQualityLevel();
}
} // namespace SceneCaptureSensor_local_ns

View File

@ -8,6 +8,8 @@
#include "Carla/Sensor/ShaderBasedSensor.h"
#include "Carla/Actor/ActorDefinition.h"
#include "SemanticSegmentationCamera.generated.h"
/// Sensor that produces "semantic segmentation" images.

View File

@ -0,0 +1,28 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "Carla.h"
#include "Carla/Sensor/Sensor.h"
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Actor/ActorBlueprintFunctionLibrary.h"
void ASensor::Set(const FActorDescription &Description)
{
// set the tick interval of the sensor
if (Description.Variations.Contains("sensor_tick"))
{
SetActorTickInterval(
UActorBlueprintFunctionLibrary::ActorAttributeToFloat(Description.Variations["sensor_tick"],
0.0f));
}
}
void ASensor::EndPlay(EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
Stream = FDataStream();
}

View File

@ -6,14 +6,14 @@
#pragma once
#include "GameFramework/Actor.h"
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Actor/ActorBlueprintFunctionLibrary.h"
#include "Carla/Sensor/DataStream.h"
#include "GameFramework/Actor.h"
#include "Sensor.generated.h"
struct FActorDescription;
/// Base class for sensors.
UCLASS(Abstract, hidecategories = (Collision, Attachment, Actor))
class CARLA_API ASensor : public AActor
@ -22,16 +22,7 @@ class CARLA_API ASensor : public AActor
public:
virtual void Set(const FActorDescription &Description)
{
// set the tick interval of the sensor
if (Description.Variations.Contains("sensor_tick"))
{
SetActorTickInterval(
UActorBlueprintFunctionLibrary::ActorAttributeToFloat(Description.Variations["sensor_tick"],
0.0f));
}
}
virtual void Set(const FActorDescription &Description);
/// Replace the FDataStream associated with this sensor.
///
@ -41,12 +32,24 @@ public:
Stream = std::move(InStream);
}
/// Return the token that allows subscribing to this sensor's stream.
auto GetToken() const
{
return Stream.GetToken();
}
protected:
void EndPlay(EEndPlayReason::Type EndPlayReason) override;
/// Return the FDataStream associated with this sensor.
FDataStream &GetDataStream()
///
/// You need to provide a reference to self, this is necessary for template
/// deduction.
template <typename SensorT>
FAsyncDataStream GetDataStream(const SensorT &Self)
{
return Stream;
return Stream.MakeAsyncDataStream(Self);
}
private:

View File

@ -7,6 +7,8 @@
#include "Carla.h"
#include "Carla/Sensor/SensorFactory.h"
#include "Carla/Game/CarlaGameInstance.h"
#include "Carla/Game/CarlaStatics.h"
#include "Carla/Sensor/Sensor.h"
#include <compiler/disable-ue4-macros.h>
@ -109,6 +111,14 @@ FActorSpawnResult ASensorFactory::SpawnActor(
UE_LOG(LogCarla, Error, TEXT("ASensorFactory: cannot spawn sensor into an empty world."));
return {};
}
UCarlaGameInstance *GameInstance = UCarlaStatics::GetGameInstance(World);
if (GameInstance == nullptr)
{
UE_LOG(LogCarla, Error, TEXT("ASensorFactory: cannot spawn sensor, incompatible game instance."));
return {};
}
auto *Sensor = World->SpawnActorDeferred<ASensor>(
Description.Class,
Transform,
@ -122,6 +132,7 @@ FActorSpawnResult ASensorFactory::SpawnActor(
else
{
Sensor->Set(Description);
Sensor->SetDataStream(GameInstance->GetServer().OpenStream());
}
UGameplayStatics::FinishSpawningActor(Sensor, Transform);
return FActorSpawnResult{Sensor};

View File

@ -99,19 +99,18 @@ static carla::Buffer AWorldObserver_Serialize(
write_data(header);
// Write every actor.
for (auto &&pair : Registry) {
auto &&actor_view = pair.second;
check(actor_view.GetActor() != nullptr);
for (auto &&View : Registry) {
check(View.IsValid());
constexpr float TO_METERS = 1e-2;
const auto velocity = TO_METERS * actor_view.GetActor()->GetVelocity();
const auto velocity = TO_METERS * View.GetActor()->GetVelocity();
// get the angular velocity
const auto RootComponent = Cast<UPrimitiveComponent>(actor_view.GetActor()->GetRootComponent());
const auto RootComponent = Cast<UPrimitiveComponent>(View.GetActor()->GetRootComponent());
FVector angularVelocity { 0.0f, 0.0f, 0.0f };
if (RootComponent != nullptr)
angularVelocity = RootComponent->GetPhysicsAngularVelocityInDegrees();
ActorDynamicState info = {
actor_view.GetActorId(),
actor_view.GetActor()->GetActorTransform(),
View.GetActorId(),
View.GetActor()->GetActorTransform(),
carla::geom::Vector3D{velocity.X, velocity.Y, velocity.Z},
carla::geom::Vector3D{angularVelocity.X, angularVelocity.Y, angularVelocity.Z},
AWorldObserver_GetActorState(actor_view, Registry)
@ -137,11 +136,12 @@ void AWorldObserver::Tick(float DeltaSeconds)
GameTimeStamp += DeltaSeconds;
auto AsyncStream = Stream.MakeAsyncDataStream(*this);
auto buffer = AWorldObserver_Serialize(
Stream.PopBufferFromPool(),
AsyncStream.PopBufferFromPool(),
GameTimeStamp,
FPlatformTime::Seconds(),
Episode->GetActorRegistry());
Stream.Send_GameThread(*this, std::move(buffer));
}
AsyncStream.Send(*this, std::move(buffer));
}

View File

@ -22,11 +22,12 @@ class CARLA_API AWorldObserver : public AActor
public:
/// Prevent this sensor to be spawned by users.
using not_spawnable = void;
AWorldObserver(const FObjectInitializer& ObjectInitializer);
/// Set the episode that will observe.
/// Set the episode that will be observed.
void SetEpisode(UCarlaEpisode &InEpisode)
{
checkf(Episode == nullptr, TEXT("Cannot set episode twice!"));
@ -34,18 +35,19 @@ public:
}
/// Replace the Stream associated with this sensor.
///
/// @warning Do not change the stream after BeginPlay. It is not thread-safe.
void SetStream(FDataMultiStream InStream)
{
Stream = std::move(InStream);
}
auto GetStreamToken() const
/// Return the token that allows subscribing to this sensor's stream.
auto GetToken() const
{
return Stream.GetToken();
}
protected:
void Tick(float DeltaSeconds) final;
private:

View File

@ -7,15 +7,11 @@
#include "Carla.h"
#include "Carla/Server/TheNewCarlaServer.h"
#include "Carla/Sensor/Sensor.h"
#include "Carla/Util/BoundingBoxCalculator.h"
#include "Carla/Util/DebugShapeDrawer.h"
#include "Carla/Util/OpenDrive.h"
#include "Carla/Vehicle/CarlaWheeledVehicle.h"
#include "Carla/Walker/WalkerController.h"
#include "GameFramework/SpectatorPawn.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/Version.h>
#include <carla/rpc/Actor.h>
@ -24,6 +20,7 @@
#include <carla/rpc/DebugShape.h>
#include <carla/rpc/EpisodeInfo.h>
#include <carla/rpc/MapInfo.h>
#include <carla/rpc/Response.h>
#include <carla/rpc/Server.h>
#include <carla/rpc/Transform.h>
#include <carla/rpc/Vector3D.h>
@ -35,6 +32,9 @@
#include <vector>
template <typename T>
using R = carla::rpc::Response<T>;
// =============================================================================
// -- Static local functions ---------------------------------------------------
// =============================================================================
@ -45,12 +45,6 @@ static std::vector<T> MakeVectorFromTArray(const TArray<Other> &Array)
return {Array.GetData(), Array.GetData() + Array.Num()};
}
static void AttachActors(AActor *Child, AActor *Parent)
{
Child->AttachToActor(Parent, FAttachmentTransformRules::KeepRelativeTransform);
Child->SetOwner(Parent);
}
// =============================================================================
// -- FTheNewCarlaServer::FPimpl -----------------------------------------------
// =============================================================================
@ -74,174 +68,120 @@ public:
private:
void BindActions();
void RespondErrorStr(const std::string &ErrorMessage) {
UE_LOG(LogCarlaServer, Log, TEXT("Responding error, %s"), *carla::rpc::ToFString(ErrorMessage));
carla::rpc::Server::RespondError(ErrorMessage);
}
void RespondError(const FString &ErrorMessage) {
RespondErrorStr(carla::rpc::FromFString(ErrorMessage));
}
void RequireEpisode()
{
if (Episode == nullptr)
{
RespondErrorStr("episode not ready");
}
}
auto SpawnActor(const FTransform &Transform, FActorDescription Description)
{
auto Result = Episode->SpawnActorWithInfo(Transform, std::move(Description));
if (Result.Key != EActorSpawnResultStatus::Success)
{
RespondError(FActorSpawnResult::StatusToString(Result.Key));
}
check(Result.Value.IsValid());
return Result.Value;
}
void AttachActors(FActorView Child, FActorView Parent)
{
if (!Child.IsValid())
{
RespondErrorStr("unable to attach actor: child actor not found");
}
if (!Parent.IsValid())
{
RespondErrorStr("unable to attach actor: parent actor not found");
}
::AttachActors(Child.GetActor(), Parent.GetActor());
}
public:
carla::rpc::Actor SerializeActor(FActorView ActorView)
{
carla::rpc::Actor Actor;
Actor.id = ActorView.GetActorId();
if (ActorView.IsValid() && !ActorView.GetActor()->IsPendingKill())
{
Actor.parent_id = Episode->GetActorRegistry().Find(ActorView.GetActor()->GetOwner()).GetActorId();
Actor.description = *ActorView.GetActorDescription();
Actor.bounding_box = UBoundingBoxCalculator::GetActorBoundingBox(ActorView.GetActor());
Actor.semantic_tags.reserve(ActorView.GetSemanticTags().Num());
using tag_t = decltype(Actor.semantic_tags)::value_type;
for (auto &&Tag : ActorView.GetSemanticTags())
{
Actor.semantic_tags.emplace_back(static_cast<tag_t>(Tag));
}
auto *Sensor = Cast<ASensor>(ActorView.GetActor());
if (Sensor != nullptr)
{
auto Stream = GetSensorStream(ActorView, *Sensor);
const auto &Token = Stream.token();
Actor.stream_token = decltype(Actor.stream_token)(std::begin(Token.data), std::end(Token.data));
}
} else {
UE_LOG(LogCarla, Warning, TEXT("Trying to serialize invalid actor"));
}
return Actor;
}
private:
carla::streaming::Stream GetSensorStream(FActorView ActorView, ASensor &Sensor) {
auto id = ActorView.GetActorId();
auto it = _StreamMap.find(id);
if (it == _StreamMap.end()) {
UE_LOG(LogCarlaServer, Log, TEXT("Making a new sensor stream for '%s'"), *ActorView.GetActorDescription()->Id);
auto result = _StreamMap.emplace(id, StreamingServer.MakeStream());
check(result.second);
it = result.first;
Sensor.SetDataStream(it->second);
}
return it->second;
}
std::unordered_map<FActorView::IdType, carla::streaming::Stream> _StreamMap;
};
// =============================================================================
// -- FTheNewCarlaServer::FPimpl Bind Actions ----------------------------------
// -- Define helper macros -----------------------------------------------------
// =============================================================================
#if WITH_EDITOR
# define CARLA_ENSURE_GAME_THREAD() check(IsInGameThread());
#else
# define CARLA_ENSURE_GAME_THREAD()
#endif // WITH_EDITOR
#define RESPOND_ERROR(str) { \
UE_LOG(LogCarlaServer, Log, TEXT("Responding error: %s"), TEXT(str)); \
return carla::rpc::ResponseError(str); }
#define RESPOND_ERROR_FSTRING(fstr) { \
UE_LOG(LogCarlaServer, Log, TEXT("Responding error: %s"), *fstr); \
return carla::rpc::ResponseError(carla::rpc::FromFString(fstr)); }
#define REQUIRE_CARLA_EPISODE() \
CARLA_ENSURE_GAME_THREAD(); \
if (Episode == nullptr) { RESPOND_ERROR("episode not ready"); }
// =============================================================================
// -- Bind Actions -------------------------------------------------------------
// =============================================================================
void FTheNewCarlaServer::FPimpl::BindActions()
{
namespace cr = carla::rpc;
namespace cg = carla::geom;
Server.BindAsync("ping", []() { return true; });
Server.BindAsync("version", []() -> R<std::string>
{
return carla::version();
});
Server.BindAsync("version", []() -> std::string { return carla::version(); });
Server.BindSync("get_episode_info", [this]() -> cr::EpisodeInfo {
RequireEpisode();
Server.BindSync("get_episode_info", [this]() -> R<cr::EpisodeInfo>
{
REQUIRE_CARLA_EPISODE();
auto WorldObserver = Episode->GetWorldObserver();
if (WorldObserver == nullptr) {
WorldObserver = Episode->StartWorldObserver(StreamingServer.MakeMultiStream());
RESPOND_ERROR("internal error: missing world observer");
}
return {Episode->GetId(), cr::FromFString(Episode->GetMapName()), WorldObserver->GetStreamToken()};
return cr::EpisodeInfo{
Episode->GetId(),
cr::FromFString(Episode->GetMapName()),
WorldObserver->GetToken()};
});
Server.BindSync("get_map_info", [this]() -> cr::MapInfo {
RequireEpisode();
Server.BindSync("get_map_info", [this]() -> R<cr::MapInfo>
{
REQUIRE_CARLA_EPISODE();
auto FileContents = FOpenDrive::Load(Episode->GetMapName());
const auto &SpawnPoints = Episode->GetRecommendedStartTransforms();
std::vector<carla::geom::Transform> spawn_points;
spawn_points.reserve(SpawnPoints.Num());
for (const auto &Transform : SpawnPoints)
{
spawn_points.emplace_back(Transform);
}
return {
const auto &SpawnPoints = Episode->GetRecommendedSpawnPoints();
return cr::MapInfo{
cr::FromFString(Episode->GetMapName()),
cr::FromFString(FileContents),
spawn_points};
MakeVectorFromTArray<cg::Transform>(SpawnPoints)};
});
Server.BindSync("get_actor_definitions", [this]() {
RequireEpisode();
Server.BindSync("get_actor_definitions", [this]() -> R<std::vector<cr::ActorDefinition>>
{
REQUIRE_CARLA_EPISODE();
return MakeVectorFromTArray<cr::ActorDefinition>(Episode->GetActorDefinitions());
});
Server.BindSync("get_spectator", [this]() -> cr::Actor {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Episode->GetSpectatorPawn());
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to find spectator");
Server.BindSync("get_spectator", [this]() -> R<cr::Actor> {
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Episode->GetSpectatorPawn());
if (!ActorView.IsValid())
{
RESPOND_ERROR("internal error: unable to find spectator");
}
return SerializeActor(ActorView);
return Episode->SerializeActor(ActorView);
});
Server.BindSync("get_weather_parameters", [this]() -> cr::WeatherParameters {
RequireEpisode();
Server.BindSync("get_weather_parameters", [this]() -> R<cr::WeatherParameters>
{
REQUIRE_CARLA_EPISODE();
auto *Weather = Episode->GetWeather();
if (Weather == nullptr) {
RespondErrorStr("unable to find weather");
if (Weather == nullptr)
{
RESPOND_ERROR("internal error: unable to find weather");
}
return Weather->GetCurrentWeather();
});
Server.BindSync("set_weather_parameters", [this](const cr::WeatherParameters &weather) {
RequireEpisode();
Server.BindSync("set_weather_parameters", [this](
const cr::WeatherParameters &weather) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto *Weather = Episode->GetWeather();
if (Weather == nullptr) {
RespondErrorStr("unable to find weather");
if (Weather == nullptr)
{
RESPOND_ERROR("internal error: unable to find weather");
}
Weather->ApplyWeather(weather);
return R<void>::Success();
});
Server.BindSync("get_actors_by_id", [this](const std::vector<FActorView::IdType> &ids) {
RequireEpisode();
Server.BindSync("get_actors_by_id", [this](
const std::vector<FActorView::IdType> &ids) -> R<std::vector<cr::Actor>>
{
REQUIRE_CARLA_EPISODE();
std::vector<cr::Actor> Result;
Result.reserve(ids.size());
const auto &Registry = Episode->GetActorRegistry();
for (auto &&Id : ids) {
auto View = Registry.Find(Id);
if (View.IsValid()) {
Result.emplace_back(SerializeActor(View));
for (auto &&Id : ids)
{
auto View = Episode->FindActor(Id);
if (View.IsValid())
{
Result.emplace_back(Episode->SerializeActor(View));
}
}
return Result;
@ -249,301 +189,352 @@ void FTheNewCarlaServer::FPimpl::BindActions()
Server.BindSync("spawn_actor", [this](
cr::ActorDescription Description,
const cr::Transform &Transform) -> cr::Actor {
RequireEpisode();
return SerializeActor(SpawnActor(Transform, Description));
const cr::Transform &Transform) -> R<cr::Actor>
{
REQUIRE_CARLA_EPISODE();
auto Result = Episode->SpawnActorWithInfo(Transform, std::move(Description));
if (Result.Key != EActorSpawnResultStatus::Success)
{
RESPOND_ERROR_FSTRING(FActorSpawnResult::StatusToString(Result.Key));
}
if (!Result.Value.IsValid())
{
RESPOND_ERROR("internal error: actor could not be spawned");
}
return Episode->SerializeActor(Result.Value);
});
Server.BindSync("spawn_actor_with_parent", [this](
cr::ActorDescription Description,
const cr::Transform &Transform,
cr::Actor Parent) -> cr::Actor {
RequireEpisode();
auto ActorView = SpawnActor(Transform, Description);
auto ParentActorView = Episode->GetActorRegistry().Find(Parent.id);
AttachActors(ActorView, ParentActorView);
return SerializeActor(ActorView);
cr::Actor Parent) -> R<cr::Actor>
{
REQUIRE_CARLA_EPISODE();
auto Result = Episode->SpawnActorWithInfo(Transform, std::move(Description));
if (Result.Key != EActorSpawnResultStatus::Success)
{
RESPOND_ERROR_FSTRING(FActorSpawnResult::StatusToString(Result.Key));
}
if (!Result.Value.IsValid())
{
RESPOND_ERROR("internal error: actor could not be spawned");
}
auto ParentActorView = Episode->FindActor(Parent.id);
if (!ParentActorView.IsValid())
{
RESPOND_ERROR("unable to attach actor: parent actor not found");
}
Episode->AttachActors(Result.Value.GetActor(), ParentActorView.GetActor());
return Episode->SerializeActor(Result.Value);
});
Server.BindSync("destroy_actor", [this](cr::Actor Actor) {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid()) {
UE_LOG(LogCarlaServer, Warning, TEXT("unable to destroy actor: not found"));
return false;
Server.BindSync("destroy_actor", [this](cr::Actor Actor) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to destroy actor: not found");
}
return Episode->DestroyActor(ActorView.GetActor());
if (!Episode->DestroyActor(ActorView.GetActor()))
{
RESPOND_ERROR("internal error: unable to destroy actor");
}
return R<void>::Success();
});
Server.BindSync("attach_actors", [this](cr::Actor Child, cr::Actor Parent) {
RequireEpisode();
auto &Registry = Episode->GetActorRegistry();
AttachActors(Registry.Find(Child.id), Registry.Find(Parent.id));
});
Server.BindSync("get_actor_location", [this](cr::Actor Actor) -> cr::Location {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to get actor location: actor not found");
Server.BindSync("attach_actors", [this](
cr::Actor Child,
cr::Actor Parent) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ChildView = Episode->FindActor(Child.id);
if (!ChildView.IsValid())
{
RESPOND_ERROR("unable to attach actor: child actor not found");
}
return {ActorView.GetActor()->GetActorLocation()};
});
Server.BindSync("get_actor_transform", [this](cr::Actor Actor) -> cr::Transform {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to get actor transform: actor not found");
auto ParentView = Episode->FindActor(Parent.id);
if (!ParentView.IsValid())
{
RESPOND_ERROR("unable to attach actor: parent actor not found");
}
return {ActorView.GetActor()->GetActorTransform()};
});
Server.BindSync("get_actor_velocity", [this](cr::Actor Actor) -> cr::Vector3D {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to get actor velocity: actor not found");
}
return cr::Vector3D(ActorView.GetActor()->GetRootComponent()->GetComponentVelocity()).ToMeters();
});
Server.BindSync("get_actor_angular_velocity", [this](cr::Actor Actor) -> cr::Vector3D {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to get actor angular velocity: actor not found");
}
auto RootComponent = Cast<UPrimitiveComponent>(ActorView.GetActor()->GetRootComponent());
if (RootComponent == nullptr) {
RespondErrorStr("unable to get actor angular velocity: not supported by actor");
}
return cr::Vector3D(RootComponent->GetPhysicsAngularVelocityInDegrees());
});
Server.BindSync("set_actor_angular_velocity", [this](
cr::Actor Actor,
cr::Vector3D vector) {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to set actor angular velocity: actor not found");
}
auto RootComponent = Cast<UPrimitiveComponent>(ActorView.GetActor()->GetRootComponent());
if (RootComponent == nullptr) {
RespondErrorStr("unable to set actor angular velocity: not supported by actor");
}
RootComponent->SetPhysicsAngularVelocityInDegrees(
vector,
false,
"None");
});
Server.BindSync("set_actor_velocity", [this](
cr::Actor Actor,
cr::Vector3D vector) {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to set actor velocity: actor not found");
}
auto RootComponent = Cast<UPrimitiveComponent>(ActorView.GetActor()->GetRootComponent());
if (RootComponent == nullptr) {
RespondErrorStr("unable to set actor velocity: not supported by actor");
}
RootComponent->SetPhysicsLinearVelocity(
vector.ToCentimeters(),
false,
"None");
});
Server.BindSync("add_actor_impulse", [this](
cr::Actor Actor,
cr::Vector3D vector) {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to add actor impulse: actor not found");
}
auto RootComponent = Cast<UPrimitiveComponent>(ActorView.GetActor()->GetRootComponent());
if (RootComponent == nullptr) {
RespondErrorStr("unable to add actor impulse: not supported by actor");
}
RootComponent->AddImpulse(
vector.ToCentimeters(),
"None",
false);
Episode->AttachActors(ChildView.GetActor(), ParentView.GetActor());
return R<void>::Success();
});
Server.BindSync("set_actor_location", [this](
cr::Actor Actor,
cr::Location Location) {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to set actor location: actor not found");
cr::Location Location) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to set actor location: actor not found");
}
// This function only works with teleport physics, to reset speeds we need
// another method.
/// @todo print error instead of returning false.
ActorView.GetActor()->SetActorRelativeLocation(
Location,
false,
nullptr,
ETeleportType::TeleportPhysics);
return R<void>::Success();
});
Server.BindSync("set_actor_transform", [this](
cr::Actor Actor,
cr::Transform Transform) {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to set actor transform: actor not found");
cr::Transform Transform) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to set actor transform: actor not found");
}
// This function only works with teleport physics, to reset speeds we need
// another method.
ActorView.GetActor()->SetActorRelativeTransform(
Transform,
false,
nullptr,
ETeleportType::TeleportPhysics);
return R<void>::Success();
});
Server.BindSync("set_actor_simulate_physics", [this](cr::Actor Actor, bool bEnabled) {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to set actor simulate physics: actor not found");
Server.BindSync("set_actor_velocity", [this](
cr::Actor Actor,
cr::Vector3D vector) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to set actor velocity: actor not found");
}
auto RootComponent = Cast<UPrimitiveComponent>(ActorView.GetActor()->GetRootComponent());
if (RootComponent == nullptr) {
RespondErrorStr("unable to set actor simulate physics: not supported by actor");
if (RootComponent == nullptr)
{
RESPOND_ERROR("unable to set actor velocity: not supported by actor");
}
RootComponent->SetPhysicsLinearVelocity(
vector.ToCentimeters(),
false,
"None");
return R<void>::Success();
});
Server.BindSync("set_actor_angular_velocity", [this](
cr::Actor Actor,
cr::Vector3D vector) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to set actor angular velocity: actor not found");
}
auto RootComponent = Cast<UPrimitiveComponent>(ActorView.GetActor()->GetRootComponent());
if (RootComponent == nullptr)
{
RESPOND_ERROR("unable to set actor angular velocity: not supported by actor");
}
RootComponent->SetPhysicsAngularVelocityInDegrees(
vector,
false,
"None");
return R<void>::Success();
});
Server.BindSync("add_actor_impulse", [this](
cr::Actor Actor,
cr::Vector3D vector) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to add actor impulse: actor not found");
}
auto RootComponent = Cast<UPrimitiveComponent>(ActorView.GetActor()->GetRootComponent());
if (RootComponent == nullptr)
{
RESPOND_ERROR("unable to add actor impulse: not supported by actor");
}
RootComponent->AddImpulse(
vector.ToCentimeters(),
"None",
false);
return R<void>::Success();
});
Server.BindSync("set_actor_simulate_physics", [this](
cr::Actor Actor,
bool bEnabled) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to set actor simulate physics: actor not found");
}
auto RootComponent = Cast<UPrimitiveComponent>(ActorView.GetActor()->GetRootComponent());
if (RootComponent == nullptr)
{
RESPOND_ERROR("unable to set actor simulate physics: not supported by actor");
}
RootComponent->SetSimulatePhysics(bEnabled);
return R<void>::Success();
});
Server.BindSync("apply_control_to_vehicle", [this](cr::Actor Actor, cr::VehicleControl Control) {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to apply control: actor not found");
Server.BindSync("apply_control_to_vehicle", [this](
cr::Actor Actor,
cr::VehicleControl Control) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to apply control: actor not found");
}
auto Vehicle = Cast<ACarlaWheeledVehicle>(ActorView.GetActor());
if (Vehicle == nullptr) {
RespondErrorStr("unable to apply control: actor is not a vehicle");
if (Vehicle == nullptr)
{
RESPOND_ERROR("unable to apply control: actor is not a vehicle");
}
Vehicle->ApplyVehicleControl(Control);
return R<void>::Success();
});
Server.BindSync("apply_control_to_walker", [this](cr::Actor Actor, cr::WalkerControl Control) {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to apply control: actor not found");
Server.BindSync("apply_control_to_walker", [this](
cr::Actor Actor,
cr::WalkerControl Control) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to apply control: actor not found");
}
auto Pawn = Cast<APawn>(ActorView.GetActor());
if (Pawn == nullptr) {
RespondErrorStr("unable to apply control: actor is not a walker");
if (Pawn == nullptr)
{
RESPOND_ERROR("unable to apply control: actor is not a walker");
}
auto Controller = Cast<AWalkerController>(Pawn->GetController());
if (Controller == nullptr) {
RespondErrorStr("unable to apply control: walker has an incompatible controller");
if (Controller == nullptr)
{
RESPOND_ERROR("unable to apply control: walker has an incompatible controller");
}
Controller->ApplyWalkerControl(Control);
return R<void>::Success();
});
Server.BindSync("set_actor_autopilot", [this](cr::Actor Actor, bool bEnabled) {
RequireEpisode();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to set autopilot: actor not found");
Server.BindSync("set_actor_autopilot", [this](
cr::Actor Actor,
bool bEnabled) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to set autopilot: actor not found");
}
auto Vehicle = Cast<ACarlaWheeledVehicle>(ActorView.GetActor());
if (Vehicle == nullptr) {
RespondErrorStr("unable to set autopilot: actor is not a vehicle");
if (Vehicle == nullptr)
{
RESPOND_ERROR("unable to set autopilot: actor does not support autopilot");
}
auto Controller = Cast<AWheeledVehicleAIController>(Vehicle->GetController());
if (Controller == nullptr) {
RespondErrorStr("unable to set autopilot: vehicle has an incompatible controller");
if (Controller == nullptr)
{
RESPOND_ERROR("unable to set autopilot: vehicle controller does not support autopilot");
}
Controller->SetAutopilot(bEnabled);
return R<void>::Success();
});
Server.BindSync("set_traffic_light_state", [this](cr::Actor Actor, cr::TrafficLightState trafficLightState) {
RequireEpisode();
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to set state: actor not found");
RESPOND_ERROR("unable to set state: actor not found");
}
auto TrafficLight = Cast<ATrafficLightBase>(ActorView.GetActor());
if (TrafficLight == nullptr) {
RespondErrorStr("unable to set state: actor is not a traffic light");
RESPOND_ERROR("unable to set state: actor is not a traffic light");
}
TrafficLight->SetTrafficLightState(static_cast<ETrafficLightState>(trafficLightState));
});
Server.BindSync("set_traffic_light_green_time", [this](cr::Actor Actor, float GreenTime) {
RequireEpisode();
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to set green time: actor not found");
RESPOND_ERROR("unable to set green time: actor not found");
}
auto TrafficLight = Cast<ATrafficLightBase>(ActorView.GetActor());
if (TrafficLight == nullptr) {
RespondErrorStr("unable to set green time: actor is not a traffic light");
RESPOND_ERROR("unable to set green time: actor is not a traffic light");
}
TrafficLight->SetGreenTime(GreenTime);
});
Server.BindSync("set_traffic_light_yellow_time", [this](cr::Actor Actor, float YellowTime) {
RequireEpisode();
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to set yellow time: actor not found");
RESPOND_ERROR("unable to set yellow time: actor not found");
}
auto TrafficLight = Cast<ATrafficLightBase>(ActorView.GetActor());
if (TrafficLight == nullptr) {
RespondErrorStr("unable to set yellow time: actor is not a traffic light");
RESPOND_ERROR("unable to set yellow time: actor is not a traffic light");
}
TrafficLight->SetYellowTime(YellowTime);
});
Server.BindSync("set_traffic_light_red_time", [this](cr::Actor Actor, float RedTime) {
RequireEpisode();
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to set red time: actor not found");
RESPOND_ERROR("unable to set red time: actor not found");
}
auto TrafficLight = Cast<ATrafficLightBase>(ActorView.GetActor());
if (TrafficLight == nullptr) {
RespondErrorStr("unable to set red time: actor is not a traffic light");
RESPOND_ERROR("unable to set red time: actor is not a traffic light");
}
TrafficLight->SetRedTime(RedTime);
});
Server.BindSync("freeze_traffic_light", [this](cr::Actor Actor, bool Freeze) {
RequireEpisode();
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->GetActorRegistry().Find(Actor.id);
if (!ActorView.IsValid() || ActorView.GetActor()->IsPendingKill()) {
RespondErrorStr("unable to alter frozen state: actor not found");
RESPOND_ERROR("unable to alter frozen state: actor not found");
}
auto TrafficLight = Cast<ATrafficLightBase>(ActorView.GetActor());
if (TrafficLight == nullptr) {
RespondErrorStr("unable to alter frozen state: actor is not a traffic light");
RESPOND_ERROR("unable to alter frozen state: actor is not a traffic light");
}
TrafficLight->SetTimeIsFrozen(Freeze);
});
Server.BindSync("draw_debug_shape", [this](const cr::DebugShape &shape) {
RequireEpisode();
Server.BindSync("draw_debug_shape", [this](const cr::DebugShape &shape) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto *World = Episode->GetWorld();
check(World != nullptr);
FDebugShapeDrawer Drawer(*World);
Drawer.Draw(shape);
return R<void>::Success();
});
}
// =============================================================================
// -- Undef helper macros ------------------------------------------------------
// =============================================================================
#undef REQUIRE_CARLA_EPISODE
#undef RESPOND_ERROR_FSTRING
#undef RESPOND_ERROR
#undef CARLA_ENSURE_GAME_THREAD
// =============================================================================
// -- FTheNewCarlaServer -------------------------------------------------------
// =============================================================================
@ -560,17 +551,20 @@ void FTheNewCarlaServer::Start(uint16_t Port)
void FTheNewCarlaServer::NotifyBeginEpisode(UCarlaEpisode &Episode)
{
check(Pimpl != nullptr);
UE_LOG(LogCarlaServer, Log, TEXT("New episode '%s' started"), *Episode.GetMapName());
Pimpl->Episode = &Episode;
}
void FTheNewCarlaServer::NotifyEndEpisode()
{
check(Pimpl != nullptr);
Pimpl->Episode = nullptr;
}
void FTheNewCarlaServer::AsyncRun(uint32 NumberOfWorkerThreads)
{
check(Pimpl != nullptr);
/// @todo Define better the number of threads each server gets.
auto RPCThreads = NumberOfWorkerThreads / 2u;
auto StreamingThreads = NumberOfWorkerThreads - RPCThreads;
@ -585,10 +579,18 @@ void FTheNewCarlaServer::RunSome(uint32 Milliseconds)
void FTheNewCarlaServer::Stop()
{
check(Pimpl != nullptr);
Pimpl->Server.Stop();
}
carla::rpc::Actor FTheNewCarlaServer::SerializeActor(FActorView View) const
FDataStream FTheNewCarlaServer::OpenStream() const
{
return Pimpl->SerializeActor(View);
}
check(Pimpl != nullptr);
return Pimpl->StreamingServer.MakeStream();
}
FDataMultiStream FTheNewCarlaServer::OpenMultiStream() const
{
check(Pimpl != nullptr);
return Pimpl->StreamingServer.MakeMultiStream();
}

View File

@ -7,13 +7,10 @@
#pragma once
#include "Carla/Actor/ActorView.h"
#include "Carla/Sensor/DataStream.h"
#include "CoreMinimal.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/rpc/Actor.h>
#include <compiler/enable-ue4-macros.h>
class UCarlaEpisode;
class FTheNewCarlaServer
@ -36,8 +33,9 @@ public:
void Stop();
// This is necessary for serializing sensors properly.
carla::rpc::Actor SerializeActor(FActorView View) const;
FDataStream OpenStream() const;
FDataMultiStream OpenMultiStream() const;
private:

View File

@ -106,5 +106,5 @@ private:
void FDebugShapeDrawer::Draw(const carla::rpc::DebugShape &Shape)
{
auto Visitor = FShapeVisitor(World, Shape.color, Shape.life_time, Shape.persistent_lines);
Shape.primitive.ApplyVisitor(Visitor);
boost::apply_visitor(Visitor, Shape.primitive);
}

View File

@ -119,6 +119,11 @@ if ${BUILD_CARLAUE4} ; then
log "Build CarlaUE4 project."
make CarlaUE4Editor
#Providing the user with the ExportedMaps folder
EXPORTED_MAPS="${CARLAUE4_ROOT_FOLDER}/Content/Carla/ExportedMaps"
mkdir -p "${EXPORTED_MAPS}"
fi
# ==============================================================================

View File

@ -37,6 +37,7 @@ LIBCARLA_RELEASE=false
LIBCARLA_DEBUG=false
PYTHON_API_2=false
PYTHON_API_3=false
RUN_BENCHMARK=false
OPTS=`getopt -o h --long help,gdb,xml,gtest_args:,all,libcarla-release,libcarla-debug,python-api-2,python-api-3,benchmark -n 'parse-options' -- "$@"`
@ -75,6 +76,7 @@ while true; do
shift ;;
--benchmark )
LIBCARLA_RELEASE=true;
RUN_BENCHMARK=true;
GTEST_ARGS="--gtest_filter=benchmark*";
shift ;;
-h | --help )
@ -103,11 +105,13 @@ if ${LIBCARLA_DEBUG} ; then
EXTRA_ARGS=
fi
log "Running LibCarla unit tests debug."
log "Running LibCarla.server unit tests (debug)."
echo "Running: ${GDB} libcarla_test_server_debug ${GTEST_ARGS} ${EXTRA_ARGS}"
LD_LIBRARY_PATH=${LIBCARLA_INSTALL_SERVER_FOLDER}/lib ${GDB} ${LIBCARLA_INSTALL_SERVER_FOLDER}/test/libcarla_test_server_debug ${GTEST_ARGS} ${EXTRA_ARGS}
echo "Running: ${GDB} libcarla_test_debug ${GTEST_ARGS} ${EXTRA_ARGS}"
LD_LIBRARY_PATH=${LIBCARLA_INSTALL_SERVER_FOLDER}/lib ${GDB} ${LIBCARLA_INSTALL_SERVER_FOLDER}/test/libcarla_test_debug ${GTEST_ARGS} ${EXTRA_ARGS}
log "Running LibCarla.client unit tests (debug)."
echo "Running: ${GDB} libcarla_test_client_debug ${GTEST_ARGS} ${EXTRA_ARGS}"
${GDB} ${LIBCARLA_INSTALL_CLIENT_FOLDER}/test/libcarla_test_client_debug ${GTEST_ARGS} ${EXTRA_ARGS}
fi
@ -119,11 +123,17 @@ if ${LIBCARLA_RELEASE} ; then
EXTRA_ARGS=
fi
log "Running LibCarla unit tests release."
log "Running LibCarla.server unit tests (release)."
echo "Running: ${GDB} libcarla_test_server_release ${GTEST_ARGS} ${EXTRA_ARGS}"
LD_LIBRARY_PATH=${LIBCARLA_INSTALL_SERVER_FOLDER}/lib ${GDB} ${LIBCARLA_INSTALL_SERVER_FOLDER}/test/libcarla_test_server_release ${GTEST_ARGS} ${EXTRA_ARGS}
echo "Running: ${GDB} libcarla_test_release ${GTEST_ARGS} ${EXTRA_ARGS}"
if ! { ${RUN_BENCHMARK} ; }; then
LD_LIBRARY_PATH=${LIBCARLA_INSTALL_SERVER_FOLDER}/lib ${GDB} ${LIBCARLA_INSTALL_SERVER_FOLDER}/test/libcarla_test_release ${GTEST_ARGS} ${EXTRA_ARGS}
log "Running LibCarla.client unit tests (release)."
echo "Running: ${GDB} libcarla_test_client_debug ${GTEST_ARGS} ${EXTRA_ARGS}"
${GDB} ${LIBCARLA_INSTALL_CLIENT_FOLDER}/test/libcarla_test_client_release ${GTEST_ARGS} ${EXTRA_ARGS}
fi
fi

142
Util/BuildTools/ExportMaps.sh Executable file
View File

@ -0,0 +1,142 @@
#! /bin/bash
# ==============================================================================
# -- Set up environment --------------------------------------------------------
# ==============================================================================
source $(dirname "$0")/Environment.sh
if [ ! -d "${UE4_ROOT}" ]; then
fatal_error "UE4_ROOT is not defined, or points to a non-existant directory, please set this environment variable."
else
log "Using Unreal Engine at '$UE4_ROOT'"
fi
# ==============================================================================
# -- Parse arguments -----------------------------------------------------------
# ==============================================================================
DOC_STRING="Build and packs CarlaUE4's ExportedMaps"
USAGE_STRING="Usage: $0 [-h|--help] [-d|--dir] <outdir> [-f|--file] <filename>"
OUTPUT_DIRECTORY=""
FILE_NAME=""
OPTS=`getopt -o h,d::,f --long help,dir::,file:: -n 'parse-options' -- "$@"`
if [ $? != 0 ] ; then echo "$USAGE_STRING" ; exit 2; fi
eval set -- "$OPTS"
while true; do
case "$1" in
--dir )
OUTPUT_DIRECTORY="$2"
shift ;;
--file )
FILE_NAME="$2"
shift ;;
-h | --help )
echo "$DOC_STRING"
echo "$USAGE_STRING"
exit 1
;;
* )
break ;;
esac
done
if [ -z "${OUTPUT_DIRECTORY}" ]; then
OUTPUT_DIRECTORY="${PWD}/ExportedMaps"
fi
if [ -z "${FILE_NAME}" ]; then
FILE_NAME="CookedExportedMaps"
fi
# ==============================================================================
# -- Package project -----------------------------------------------------------
# ==============================================================================
REPOSITORY_TAG=$(get_carla_version)
BUILD_FOLDER=${OUTPUT_DIRECTORY}
log "Packaging user content from version '$REPOSITORY_TAG'."
rm -Rf ${BUILD_FOLDER}
mkdir -p ${BUILD_FOLDER}
pushd "${CARLAUE4_ROOT_FOLDER}" > /dev/null
log "Current project directory: '${PWD}'"
MAP_LIST=""
for filepath in `find ${PWD}/Content/ -type f -name "*.umap"`; do
if [[ $filepath == *"/ExportedMaps/"* ]]; then
filepath="/Game/"${filepath#"${PWD}/Content/"}
if [ -z "${MAP_LIST}" ]; then
MAP_LIST=$filepath
else
MAP_LIST=$MAP_LIST+$filepath
fi
fi
done
echo $MAP_LIST
${UE4_ROOT}/Engine/Build/BatchFiles/RunUAT.sh BuildCookRun \
-project="${PWD}/CarlaUE4.uproject" -map=$MAP_LIST \
-nocompileeditor -nop4 -cook \
-archive -package -stage -nodebuginfo -build \
-clientconfig=Development -ue4exe=UE4Editor \
-targetplatform=Linux -CrashReporter -utf8output \
-SkipCookingEditorContent -archivedirectory="${BUILD_FOLDER}"
popd >/dev/null
if [[ ! -d ${BUILD_FOLDER}/LinuxNoEditor ]] ; then
fatal_error "Failed to package the project!"
fi
# ==============================================================================
# -- Zip the project -----------------------------------------------------------
# ==============================================================================
DESTINATION=${BUILD_FOLDER}/${FILE_NAME}.tar.gz
SOURCE=${BUILD_FOLDER}
pushd "${SOURCE}/LinuxNoEditor" >/dev/null
log "Packaging build."
rm -Rf ./CarlaUE4/Saved
rm -Rf ./Engine
rm ./CarlaUE4.sh
rm ./Manifest_NonUFSFiles_Linux.txt
rm ./Manifest_UFSFiles_Linux.txt
rm -Rf ./CarlaUE4/Binaries
rm -Rf ./CarlaUE4/Config
rm -Rf ./CarlaUE4/Plugins
rm ./CarlaUE4/AssetRegistry.bin
rm ./CarlaUE4/CarlaUE4.uproject
tar -czvf ${DESTINATION} *
popd > /dev/null
# ==============================================================================
# -- Remove intermediate files and return everything to normal------------------
# ==============================================================================
log "Removing intermediate build."
rm -Rf ${BUILD_FOLDER}/LinuxNoEditor
# ==============================================================================
# -- ...and we are done --------------------------------------------------------
# ==============================================================================
log "ExportedMaps created at ${DESTINATION}"
log "Success!"

View File

@ -32,16 +32,19 @@ hard-clean:
@${CARLA_BUILD_TOOLS_FOLDER}/BuildLibCarla.sh --clean
@echo "To force recompiling dependencies run: rm -Rf ${CARLA_BUILD_FOLDER}"
check: LibCarla.server PythonAPI
export-maps:
@${CARLA_BUILD_TOOLS_FOLDER}/ExportMaps.sh ${ARGS}
check: LibCarla PythonAPI
@${CARLA_BUILD_TOOLS_FOLDER}/Check.sh --all $(ARGS)
check.LibCarla: LibCarla.server
check.LibCarla: LibCarla
@${CARLA_BUILD_TOOLS_FOLDER}/Check.sh --libcarla-debug --libcarla-release $(ARGS)
check.LibCarla.debug: LibCarla.server
check.LibCarla.debug: LibCarla
@${CARLA_BUILD_TOOLS_FOLDER}/Check.sh --libcarla-debug $(ARGS)
check.LibCarla.release: LibCarla.server
check.LibCarla.release: LibCarla
@${CARLA_BUILD_TOOLS_FOLDER}/Check.sh --libcarla-release $(ARGS)
check.PythonAPI: PythonAPI

View File

@ -41,6 +41,10 @@ Use the following commands:
modified since the precompiled header" errors. Beware, recompilation
takes a long time!
export-maps:
Export maps from /Game/Carla/ExportedMaps to the ExportedMaps folder
at the root of the Carla project.
There are also some lower level commands for building individual modules helpful
for developers:

View File

@ -69,8 +69,8 @@ if $DO_PACKAGE ; then
-project="${PWD}/CarlaUE4.uproject" \
-nocompileeditor -nop4 -cook -stage -archive -package \
-clientconfig=Development -ue4exe=UE4Editor \
-pak -prereqs -nodebuginfo \
-targetplatform=Linux -build -CrashReporter -utf8output \
-prereqs -nodebuginfo \
-targetplatform=Linux -build -utf8output \
-archivedirectory="${BUILD_FOLDER}"
popd >/dev/null
@ -93,6 +93,8 @@ if $DO_COPY_FILES ; then
pushd ${CARLA_ROOT_FOLDER} >/dev/null
mkdir -p "${DESTINATION}/ExportedMaps"
copy_if_changed "./LICENSE" "${DESTINATION}/LICENSE"
copy_if_changed "./CHANGELOG.md" "${DESTINATION}/CHANGELOG"
copy_if_changed "./Docs/release_readme.md" "${DESTINATION}/README"
@ -107,6 +109,7 @@ if $DO_COPY_FILES ; then
copy_if_changed "./PythonAPI/spawn_npc.py" "${DESTINATION}/spawn_npc.py"
copy_if_changed "./PythonAPI/tutorial.py" "${DESTINATION}/tutorial.py"
copy_if_changed "./PythonAPI/vehicle_gallery.py" "${DESTINATION}/vehicle_gallery.py"
copy_if_changed "./Util/ImportMaps.sh" "${DESTINATION}/ImportMaps.sh"
popd >/dev/null

View File

@ -22,7 +22,7 @@ pushd ${CARLA_BUILD_FOLDER} >/dev/null
# -- Get and compile libc++ ----------------------------------------------------
# ==============================================================================
LLVM_BASENAME=llvm-6.0
LLVM_BASENAME=llvm-6.0-ex
LLVM_INCLUDE=${PWD}/${LLVM_BASENAME}-install/include/c++/v1
LLVM_LIBPATH=${PWD}/${LLVM_BASENAME}-install/lib
@ -45,7 +45,9 @@ else
pushd ${LLVM_BASENAME}-build >/dev/null
cmake -G "Ninja" \
-DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=OFF -DLIBCXX_INSTALL_EXPERIMENTAL_LIBRARY=OFF \
-DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=OFF \
-DLIBCXX_INSTALL_EXPERIMENTAL_LIBRARY=OFF \
-DLLVM_ENABLE_EH=OFF \
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX="../${LLVM_BASENAME}-install" \
../${LLVM_BASENAME}-source
@ -57,10 +59,6 @@ else
popd >/dev/null
# Workaround, it seems LLVM 5.0 does not install these files.
# cp -v ${LLVM_BASENAME}-build/include/c++/v1/cxxabi.h ${LLVM_INCLUDE}
# cp -v ${LLVM_BASENAME}-build/include/c++/v1/__cxxabi_config.h ${LLVM_INCLUDE}
rm -Rf ${LLVM_BASENAME}-source ${LLVM_BASENAME}-build
fi
@ -155,7 +153,7 @@ unset BOOST_BASENAME
# -- Get rpclib and compile it with libc++ and libstdc++ -----------------------
# ==============================================================================
RPCLIB_BASENAME=rpclib-d1146b7
RPCLIB_BASENAME=rpclib-d1146b7-ex
RPCLIB_LIBCXX_INCLUDE=${PWD}/${RPCLIB_BASENAME}-libcxx-install/include
RPCLIB_LIBCXX_LIBPATH=${PWD}/${RPCLIB_BASENAME}-libcxx-install/lib
@ -188,7 +186,7 @@ else
pushd ${RPCLIB_BASENAME}-libcxx-build >/dev/null
cmake -G "Ninja" \
-DCMAKE_CXX_FLAGS="-fPIC -std=c++14 -stdlib=libc++ -I${LLVM_INCLUDE} -Wl,-L${LLVM_LIBPATH}" \
-DCMAKE_CXX_FLAGS="-fPIC -std=c++14 -stdlib=libc++ -I${LLVM_INCLUDE} -Wl,-L${LLVM_LIBPATH} -DBOOST_NO_EXCEPTIONS -DASIO_NO_EXCEPTIONS" \
-DCMAKE_INSTALL_PREFIX="../${RPCLIB_BASENAME}-libcxx-install" \
../${RPCLIB_BASENAME}-source
@ -225,29 +223,34 @@ unset RPCLIB_BASENAME
# -- Get GTest and compile it with libc++ --------------------------------------
# ==============================================================================
GTEST_BASENAME=googletest-1.8.0
GTEST_BASENAME=googletest-1.8.0-ex
GTEST_INCLUDE=${PWD}/${GTEST_BASENAME}-install/include
GTEST_LIBPATH=${PWD}/${GTEST_BASENAME}-install/lib
GTEST_LIBCXX_INCLUDE=${PWD}/${GTEST_BASENAME}-libcxx-install/include
GTEST_LIBCXX_LIBPATH=${PWD}/${GTEST_BASENAME}-libcxx-install/lib
GTEST_LIBSTDCXX_INCLUDE=${PWD}/${GTEST_BASENAME}-libstdcxx-install/include
GTEST_LIBSTDCXX_LIBPATH=${PWD}/${GTEST_BASENAME}-libstdcxx-install/lib
if [[ -d "${GTEST_BASENAME}-install" ]] ; then
if [[ -d "${GTEST_BASENAME}-libcxx-install" && -d "${GTEST_BASENAME}-libstdcxx-install" ]] ; then
log "${GTEST_BASENAME} already installed."
else
rm -Rf ${GTEST_BASENAME}-source ${GTEST_BASENAME}-build
rm -Rf \
${GTEST_BASENAME}-source \
${GTEST_BASENAME}-libcxx-build ${GTEST_BASENAME}-libstdcxx-build \
${GTEST_BASENAME}-libcxx-install ${GTEST_BASENAME}-libstdcxx-install
log "Retrieving Google Test."
git clone --depth=1 -b release-1.8.0 https://github.com/google/googletest.git ${GTEST_BASENAME}-source
log "Building Google Test."
log "Building Google Test with libc++."
mkdir -p ${GTEST_BASENAME}-build
mkdir -p ${GTEST_BASENAME}-libcxx-build
pushd ${GTEST_BASENAME}-build >/dev/null
pushd ${GTEST_BASENAME}-libcxx-build >/dev/null
cmake -G "Ninja" \
-DCMAKE_CXX_FLAGS="-std=c++14 -stdlib=libc++ -I${LLVM_INCLUDE} -Wl,-L${LLVM_LIBPATH}" \
-DCMAKE_INSTALL_PREFIX="../${GTEST_BASENAME}-install" \
-DCMAKE_CXX_FLAGS="-std=c++14 -stdlib=libc++ -I${LLVM_INCLUDE} -Wl,-L${LLVM_LIBPATH} -DBOOST_NO_EXCEPTIONS -fno-exceptions" \
-DCMAKE_INSTALL_PREFIX="../${GTEST_BASENAME}-libcxx-install" \
../${GTEST_BASENAME}-source
ninja
@ -256,10 +259,29 @@ else
popd >/dev/null
rm -Rf ${GTEST_BASENAME}-source ${GTEST_BASENAME}-build
log "Building Google Test with libstdc++."
mkdir -p ${GTEST_BASENAME}-libstdcxx-build
pushd ${GTEST_BASENAME}-libstdcxx-build >/dev/null
cmake -G "Ninja" \
-DCMAKE_CXX_FLAGS="-std=c++14" \
-DCMAKE_INSTALL_PREFIX="../${GTEST_BASENAME}-libstdcxx-install" \
../${GTEST_BASENAME}-source
ninja
ninja install
popd >/dev/null
rm -Rf ${GTEST_BASENAME}-source ${GTEST_BASENAME}-libcxx-build ${GTEST_BASENAME}-libstdcxx-build
fi
unset GTEST_BASENAME
# ==============================================================================
# -- Generate CMake toolchains and config --------------------------------------
# ==============================================================================
@ -276,8 +298,6 @@ set(CMAKE_CXX_COMPILER ${CXX})
set(CMAKE_CXX_FLAGS "\${CMAKE_CXX_FLAGS} -std=c++14 -pthread -fPIC" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS "\${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra" CACHE STRING "" FORCE)
# See https://bugs.llvm.org/show_bug.cgi?id=21629
# set(CMAKE_CXX_FLAGS "\${CMAKE_CXX_FLAGS} -Wno-missing-braces" CACHE STRING "" FORCE)
# @todo These flags need to be compatible with setup.py compilation.
set(CMAKE_CXX_FLAGS_RELEASE_CLIENT "\${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -std=c++14 -Wno-missing-braces -DBOOST_ERROR_CODE_HEADER_ONLY -DLIBCARLA_ENABLE_LIFETIME_PROFILER -DLIBCARLA_WITH_PYTHON_SUPPORT" CACHE STRING "" FORCE)
@ -292,6 +312,7 @@ cat >>${LIBCPP_TOOLCHAIN_FILE}.gen <<EOL
set(CMAKE_CXX_FLAGS "\${CMAKE_CXX_FLAGS} -stdlib=libc++" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS "\${CMAKE_CXX_FLAGS} -I${LLVM_INCLUDE}" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS "\${CMAKE_CXX_FLAGS} -fno-exceptions" CACHE STRING "" FORCE)
set(CMAKE_CXX_LINK_FLAGS "\${CMAKE_CXX_LINK_FLAGS} -L${LLVM_LIBPATH}" CACHE STRING "" FORCE)
set(CMAKE_CXX_LINK_FLAGS "\${CMAKE_CXX_LINK_FLAGS} -lc++ -lc++abi" CACHE STRING "" FORCE)
EOL
@ -305,6 +326,13 @@ set(CARLA_VERSION $(get_carla_version))
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY)
if (CMAKE_BUILD_TYPE STREQUAL "Server")
add_definitions(-DASIO_NO_EXCEPTIONS)
add_definitions(-DBOOST_NO_EXCEPTIONS)
add_definitions(-DLIBCARLA_NO_EXCEPTIONS)
add_definitions(-DPUGIXML_NO_EXCEPTIONS)
endif ()
# Uncomment to force support for an specific image format (require their
# respective libraries installed).
# add_definitions(-DLIBCARLA_IMAGE_WITH_PNG_SUPPORT)
@ -317,14 +345,16 @@ if (CMAKE_BUILD_TYPE STREQUAL "Server")
# Here libraries linking libc++.
set(LLVM_INCLUDE_PATH "${LLVM_INCLUDE}")
set(LLVM_LIB_PATH "${LLVM_LIBPATH}")
set(GTEST_INCLUDE_PATH "${GTEST_INCLUDE}")
set(GTEST_LIB_PATH "${GTEST_LIBPATH}")
set(RPCLIB_INCLUDE_PATH "${RPCLIB_LIBCXX_INCLUDE}")
set(RPCLIB_LIB_PATH "${RPCLIB_LIBCXX_LIBPATH}")
set(GTEST_INCLUDE_PATH "${GTEST_LIBCXX_INCLUDE}")
set(GTEST_LIB_PATH "${GTEST_LIBCXX_LIBPATH}")
elseif (CMAKE_BUILD_TYPE STREQUAL "Client")
# Here libraries linking libstdc++.
set(RPCLIB_INCLUDE_PATH "${RPCLIB_LIBSTDCXX_INCLUDE}")
set(RPCLIB_LIB_PATH "${RPCLIB_LIBSTDCXX_LIBPATH}")
set(GTEST_INCLUDE_PATH "${GTEST_LIBSTDCXX_INCLUDE}")
set(GTEST_LIB_PATH "${GTEST_LIBSTDCXX_LIBPATH}")
set(BOOST_LIB_PATH "${BOOST_LIBPATH}")
endif ()

38
Util/ImportMaps.sh Executable file
View File

@ -0,0 +1,38 @@
#! /bin/bash
# ==============================================================================
# -- Parse arguments -----------------------------------------------------------
# ==============================================================================
DOC_STRING="Unpack and copy over CarlaUE4's Exported Maps"
USAGE_STRING="Usage: $0 [-h|--help] [-d|--dir] <outdir>"
OUTPUT_DIRECTORY=""
OPTS=`getopt -o h,d:: --long help,dir:: -n 'parse-options' -- "$@"`
if [ $? != 0 ] ; then echo "$USAGE_STRING" ; exit 2; fi
eval set -- "$OPTS"
while true; do
case "$1" in
--dir )
OUTPUT_DIRECTORY="$2"
shift ;;
-h | --help )
echo "$DOC_STRING"
echo "$USAGE_STRING"
exit 1
;;
* )
break ;;
esac
done
#Tar.gz the stuff
for filepath in `find ExportedMaps/ -type f -name "*.tar.gz"`; do
tar --keep-newer-files -xvf ${filepath}
done

View File

@ -21,6 +21,7 @@ pages:
- 'Running in a Docker': 'carla_docker.md'
- "How to make a new map with RoadRunner": 'how_to_make_a_new_map.md'
- "How to link Epic's Automotive Materials": 'epic_automotive_materials.md'
- "How to export and import maps to distribution builds": 'export_import_dist.md'
- Contributing:
- 'Contribution guidelines': 'CONTRIBUTING.md'
- 'Coding standard': 'coding_standard.md'