diff --git a/src/ecstasy/serialization/ComponentSerializer.hpp b/src/ecstasy/serialization/ComponentSerializer.hpp index 4f8975d7a..c435d5767 100644 --- a/src/ecstasy/serialization/ComponentSerializer.hpp +++ b/src/ecstasy/serialization/ComponentSerializer.hpp @@ -15,7 +15,9 @@ #include "ecstasy/config.hpp" #include "ecstasy/serialization/RawSerializer.hpp" #include "util/meta/Traits.hpp" +#include "ecstasy/serialization/traits/can_load_type.hpp" #include "ecstasy/serialization/traits/can_save_type.hpp" +#include "ecstasy/serialization/traits/can_update_type.hpp" #ifndef ECSTASY_SERIALIZERS #define ECSTASY_SERIALIZERS util::meta::Traits @@ -48,9 +50,9 @@ namespace ecstasy::serialization template struct ComponentSerializer> { /// - /// @brief Serialize a component with the given serializer. + /// @brief Save a component with the given serializer. /// - /// @note This function will try to serialize the component with each serializer in the list, stopping when the + /// @note This function will try to save the component with each serializer in the list, stopping when the /// targeted serializer is found (using @p stype). /// /// @tparam Comp Component type. @@ -65,14 +67,14 @@ namespace ecstasy::serialization /// @since 1.0.0 (2024-06-11) /// template - static ISerializer &serialize(ISerializer &serializer, const std::type_info &stype, const Comp &component) + static ISerializer &save(ISerializer &serializer, const std::type_info &stype, const Comp &component) { - (trySerialize(serializer, stype, component) || ... || trySerialize(serializer, stype, component)); + (trySave(serializer, stype, component) || ... || trySave(serializer, stype, component)); return serializer; } /// - /// @brief Try to serialize a component with the given serializer. The serialization is done if S is the same as + /// @brief Try to save a component with the given serializer. The serialization is done if S is the same as /// @p stype. /// /// @tparam S Serializer type. @@ -88,7 +90,7 @@ namespace ecstasy::serialization /// @since 1.0.0 (2024-06-11) /// template - static bool trySerialize(ISerializer &serializer, const std::type_info &stype, const Comp &component) + static bool trySave(ISerializer &serializer, const std::type_info &stype, const Comp &component) { if constexpr (traits::can_save_type_v) { if (stype == typeid(S)) { @@ -98,10 +100,112 @@ namespace ecstasy::serialization } return false; } + + /// + /// @brief Update a component with the given serializer. + /// + /// @tparam Comp Component type. + /// + /// @param[in] serializer Serializer to use. + /// @param[in] stype Type of the serializer. + /// @param[in] component Component to update. + /// + /// @return ISerializer& The serializer. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-25) + /// + template + static ISerializer &update(ISerializer &serializer, const std::type_info &stype, Comp &component) + { + (tryUpdate(serializer, stype, component) || ... || tryUpdate(serializer, stype, component)); + return serializer; + } + + /// + /// @brief Try to update a component with the given serializer. + /// + /// @tparam S Serializer type. + /// @tparam Comp Component type. + /// + /// @param[in] serializer Serializer to use. + /// @param[in] stype Type of the serializer. + /// @param[in] component Component to update. + /// + /// @return bool True if the component was updated, false otherwise. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-25) + /// + template + static bool tryUpdate(ISerializer &serializer, const std::type_info &stype, Comp &component) + { + if constexpr (traits::can_update_type_v) { + if (stype == typeid(S)) { + dynamic_cast(serializer).update(component); + return true; + } + } + return false; + } + + /// + /// @brief Load a component with the given serializer for a specifc entity. + /// + /// @tparam Storage Storage type. + /// + /// @param[in] serializer Serializer to use. + /// @param[in] stype Type of the serializer. + /// @param[in] storage Storage to use. + /// @param[in] entityId Entity id to which the storage must be loaded. + /// + /// @return ISerializer& The serializer. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-25) + /// + template + static ISerializer &load( + ISerializer &serializer, const std::type_info &stype, Storage &storage, size_t entityId) + { + (tryLoad(serializer, stype, storage, entityId) || ... + || tryLoad(serializer, stype, storage, entityId)); + return serializer; + } + + /// + /// @brief Try to load a component with the given serializer for a specific entity. + /// + /// @tparam S Serializer type. + /// @tparam Storage Storage type. + /// + /// @param[in] serializer Serializer to use. + /// @param[in] stype Type of the serializer. + /// @param[in] storage Storage to use. + /// @param[in] entityId Entity id to which the storage must be loaded. + /// + /// @return bool True if the component was loaded, false otherwise. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-25) + /// + template + static bool tryLoad(ISerializer &serializer, const std::type_info &stype, Storage &storage, size_t entityId) + { + if constexpr (traits::can_load_type_v + && std::movable) { + if (stype == typeid(S)) { + storage.insert( + entityId, dynamic_cast(serializer).template load()); + return true; + } + } + return false; + } }; /// - /// @brief Serialize a component with the given serializer if possible. + /// @brief Save a component with the given serializer if possible. /// /// @tparam C Component type. /// @@ -115,9 +219,50 @@ namespace ecstasy::serialization /// @since 1.0.0 (2024-06-11) /// template - ISerializer &serialize(ISerializer &serializer, const std::type_info &stype, const C &component) + ISerializer &save(ISerializer &serializer, const std::type_info &stype, const C &component) + { + return ComponentSerializer::save(serializer, stype, component); + } + + /// + /// @brief Update a component with the given serializer if possible. + /// + /// @tparam C Component type. + /// + /// @param[in] serializer Serializer to use. + /// @param[in] stype Type of the serializer. + /// @param[in] component Component to update. + /// + /// @return ISerializer& The serializer. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-25) + /// + template + ISerializer &update(ISerializer &serializer, const std::type_info &stype, C &component) + { + return ComponentSerializer::update(serializer, stype, component); + } + + /// + /// @brief Load a component with the given serializer if possible. + /// + /// @tparam Storage Storage type. + /// + /// @param[in] serializer Serializer to use. + /// @param[in] stype Type of the serializer. + /// @param[in] storage Storage to use. + /// @param[in] entityId Entity id to which the storage must be loaded. + /// + /// @return ISerializer& The serializer. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-25) + /// + template + ISerializer &load(ISerializer &serializer, const std::type_info &stype, Storage &storage, size_t entityId) { - return ComponentSerializer::serialize(serializer, stype, component); + return ComponentSerializer::load(serializer, stype, storage, entityId); } } // namespace ecstasy::serialization diff --git a/src/ecstasy/serialization/RawSerializer.hpp b/src/ecstasy/serialization/RawSerializer.hpp index 55d572585..4eef364ca 100644 --- a/src/ecstasy/serialization/RawSerializer.hpp +++ b/src/ecstasy/serialization/RawSerializer.hpp @@ -54,13 +54,10 @@ namespace ecstasy::serialization /// ~RawSerializer() override = default; - /// @copydoc Serializer::save - using Serializer::save; - /// @copydoc save // clang-format off template || std::is_same_v) || // String std::is_bounded_array_v || // Bounded array util::meta::is_std_vector::value || // std::vector @@ -68,7 +65,7 @@ namespace ecstasy::serialization , int>::type > requires(!std::is_fundamental_v) // clang-format on - RawSerializer &save(const T &object) + RawSerializer &saveImpl(const T &object) { if constexpr (std::is_same_v || std::is_same_v) { save(static_cast(object.size())); @@ -91,28 +88,25 @@ namespace ecstasy::serialization /// @copydoc save template requires std::is_fundamental_v - RawSerializer &save(U object) + RawSerializer &saveImpl(U object) { write(reinterpret_cast(&object), sizeof(object)); return *this; } - /// @copydoc Serializer::update - using Serializer::update; - // clang-format off /// @copydoc update template || std::is_same_v) || // Load + typename = typename std::enable_if<( + std::is_same_v || // Load std::is_bounded_array_v || // Bounded array util::meta::is_std_vector::value) // std::vector , int>::type> // clang-format on - RawSerializer &update(U &object) + RawSerializer &updateImpl(U &object) { - if constexpr (std::is_fundamental_v || std::is_same_v) + if constexpr (std::is_same_v) object = load(); else if constexpr (std::is_bounded_array_v) for (size_t i = 0; i < std::extent_v; i++) @@ -132,13 +126,10 @@ namespace ecstasy::serialization return *this; } - /// @copydoc Serializer::load - using Serializer::load; - /// @copydoc load template requires std::is_fundamental_v || std::is_same_v - U load() + U loadImpl() { if constexpr (std::is_fundamental_v) return loadRaw(); @@ -148,8 +139,6 @@ namespace ecstasy::serialization _stream.seekg(pos + std::streamoff(size)); return std::string(_stream.view().data() + pos, size); - } else { - return Parent::load(); } } @@ -185,7 +174,7 @@ namespace ecstasy::serialization /// void write(const char *bytes, size_t size) { - _stream.write(bytes, size); + _stream.write(bytes, static_cast(size)); } /// diff --git a/src/ecstasy/serialization/Serializer.hpp b/src/ecstasy/serialization/Serializer.hpp index 022830cdd..f7722714a 100644 --- a/src/ecstasy/serialization/Serializer.hpp +++ b/src/ecstasy/serialization/Serializer.hpp @@ -16,6 +16,8 @@ #include "ecstasy/serialization/ISerializer.hpp" #include "ecstasy/serialization/concepts/has_extraction_operator.hpp" #include "ecstasy/serialization/concepts/has_insertion_operator.hpp" +#include "ecstasy/serialization/traits/can_load_type.hpp" +#include "ecstasy/serialization/traits/can_save_type.hpp" #include "ecstasy/serialization/traits/can_update_type.hpp" namespace ecstasy::serialization @@ -91,10 +93,14 @@ namespace ecstasy::serialization /// @author Andréas Leroux (andreas.leroux@epitech.eu) /// @since 1.0.0 (2024-04-30) /// - template U> + template + requires concepts::has_extraction_operator || traits::has_save_impl_for_type_v S &save(const U &object) { - return object >> inner(); + if constexpr (traits::has_save_impl_for_type_v) + return inner().saveImpl(object); + else + return object >> inner(); } /// @@ -157,7 +163,7 @@ namespace ecstasy::serialization for (auto &storage : storages) { // We send the typeid of the serializer before loosing the type information (since storage.serialize // takes an ISerializer) - storage.get().serialize(*this, typeid(S), entity.getIndex()); + storage.get().save(*this, typeid(S), entity.getIndex()); } return inner(); } @@ -176,9 +182,12 @@ namespace ecstasy::serialization /// template requires is_constructible || (std::is_default_constructible_v && traits::can_update_type_v) + || traits::has_load_impl_for_type_v U load() { - if constexpr (is_constructible) { + if constexpr (traits::has_load_impl_for_type_v) + return inner().template loadImpl(); + else if constexpr (is_constructible) { return U(inner()); } else { U object; @@ -187,6 +196,24 @@ namespace ecstasy::serialization } } + /// + /// @brief Load an entity from the serializer. + /// + /// @param[in] registry Registry to load the entity into. + /// + /// @return RegistryEntity Loaded entity. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-25) + /// + RegistryEntity loadEntity(Registry ®istry) + { + RegistryEntity entity(registry.entityBuilder().build(), registry); + + updateEntity(entity); + return entity; + } + /// /// @brief Update an existing object from the serializer. /// @@ -203,13 +230,38 @@ namespace ecstasy::serialization /// @since 1.0.0 (2024-04-30) /// template - requires std::is_fundamental_v || concepts::has_insertion_operator + requires traits::has_update_impl_for_type_v || std::is_fundamental_v + || concepts::has_insertion_operator S &update(U &object) { - if constexpr (std::is_fundamental_v) { + if constexpr (traits::has_update_impl_for_type_v) + return inner().updateImpl(object); + else if constexpr (std::is_fundamental_v) object = inner().template load(); - } else { + else object << inner(); + return inner(); + } + + /// + /// @brief Update an entity component from the serializer. + /// + /// @param[in] entity Entity to update. + /// + /// @return S& Reference to @b this for chain calls. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-25) + /// + S &updateEntity(RegistryEntity &entity) + { + std::size_t component_hash = load(); + auto &storages = entity.getRegistry().getStorages().getInner(); + + for (const auto &pair : storages) { + if (pair.second->getComponentTypeInfos().hash_code() == component_hash) { + pair.second->load(*this, typeid(S), entity.getIndex()); + } } return inner(); } diff --git a/src/ecstasy/serialization/concepts/has_extraction_operator.hpp b/src/ecstasy/serialization/concepts/has_extraction_operator.hpp index 6e9bf5fd8..fc71afc2c 100644 --- a/src/ecstasy/serialization/concepts/has_extraction_operator.hpp +++ b/src/ecstasy/serialization/concepts/has_extraction_operator.hpp @@ -20,8 +20,8 @@ namespace ecstasy::serialization::concepts /// /// @brief Concept to check if a type can be saved with a serializer using the extraction operator. /// - /// @tparam T Type to save. /// @tparam S Serializer type. + /// @tparam T Type to save. /// /// @param[in] s Serializer. /// @param[in] t Type to save. @@ -29,7 +29,7 @@ namespace ecstasy::serialization::concepts /// @author Andréas Leroux (andreas.leroux@epitech.eu) /// @since 1.0.0 (2024-06-24) /// - template + template concept has_extraction_operator = requires(S &s, const T &t) { // Cannot use is_serializer here because it would create a circular dependency. requires std::derived_from; diff --git a/src/ecstasy/serialization/traits/can_load_type.hpp b/src/ecstasy/serialization/traits/can_load_type.hpp index bbf19df9e..3eda55721 100644 --- a/src/ecstasy/serialization/traits/can_load_type.hpp +++ b/src/ecstasy/serialization/traits/can_load_type.hpp @@ -47,6 +47,34 @@ namespace ecstasy::serialization::traits template bool constexpr can_load_type_v = can_load_type::value; + /// + /// @brief Concept to check if a serializer has a specific load implementation for a type. + /// + /// @tparam S Serializer type. + /// @tparam T Type to check. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-07-16) + /// + template > + struct has_load_impl_for_type : std::false_type {}; + + /// @copydoc has_load_impl_for_type + template + struct has_load_impl_for_type().template loadImpl())>> + : std::true_type {}; + + /// + /// @brief Alias for @ref has_load_impl_for_type::value. + /// + /// @tparam S Serializer type. + /// @tparam C Component type. + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-11) + /// + template + bool constexpr has_load_impl_for_type_v = has_load_impl_for_type::value; + } // namespace ecstasy::serialization::traits #endif /* !ECSTASY_SERIALIZATION_CAN_LOAD_TYPE_HPP_ */ diff --git a/src/ecstasy/serialization/traits/can_save_type.hpp b/src/ecstasy/serialization/traits/can_save_type.hpp index 5aba38e34..9ec9e1fd8 100644 --- a/src/ecstasy/serialization/traits/can_save_type.hpp +++ b/src/ecstasy/serialization/traits/can_save_type.hpp @@ -48,6 +48,34 @@ namespace ecstasy::serialization::traits template bool constexpr can_save_type_v = can_save_type::value; + /// + /// @brief Concept to check if a serializer has a specific save implementation for a type. + /// + /// @tparam S Serializer type. + /// @tparam T Type to check. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-07-16) + /// + template > + struct has_save_impl_for_type : std::false_type {}; + + /// @copydoc has_save_impl_for_type + template + struct has_save_impl_for_type().saveImpl(std::declval()))>> + : std::true_type {}; + + /// + /// @brief Alias for @ref has_save_impl_for_type::value. + /// + /// @tparam S Serializer type. + /// @tparam C Component type. + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-11) + /// + template + bool constexpr has_save_impl_for_type_v = has_save_impl_for_type::value; + } // namespace ecstasy::serialization::traits #endif /* !ECSTASY_SERIALIZATION_CAN_SAVE_TYPE_HPP_ */ diff --git a/src/ecstasy/serialization/traits/can_update_type.hpp b/src/ecstasy/serialization/traits/can_update_type.hpp index a3b7b0104..3ca430b2d 100644 --- a/src/ecstasy/serialization/traits/can_update_type.hpp +++ b/src/ecstasy/serialization/traits/can_update_type.hpp @@ -48,6 +48,34 @@ namespace ecstasy::serialization::traits template bool constexpr can_update_type_v = can_update_type::value; + /// + /// @brief Concept to check if a serializer has a specific update implementation for a type. + /// + /// @tparam S Serializer type. + /// @tparam T Type to check. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-07-16) + /// + template > + struct has_update_impl_for_type : std::false_type {}; + + /// @copydoc has_update_impl_for_type + template + struct has_update_impl_for_type().updateImpl(std::declval()))>> + : std::true_type {}; + + /// + /// @brief Alias for @ref has_update_impl_for_type::value. + /// + /// @tparam S Serializer type. + /// @tparam C Component type. + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-11) + /// + template + bool constexpr has_update_impl_for_type_v = has_update_impl_for_type::value; + } // namespace ecstasy::serialization::traits #endif /* !ECSTASY_SERIALIZATION_CAN_UPDATE_TYPE_HPP_ */ diff --git a/src/ecstasy/storages/AStorage.hpp b/src/ecstasy/storages/AStorage.hpp index 8b2944c10..6f43d8c38 100644 --- a/src/ecstasy/storages/AStorage.hpp +++ b/src/ecstasy/storages/AStorage.hpp @@ -165,14 +165,42 @@ namespace ecstasy return (*this)[index]; }; - /// @copybrief serialize - serialization::ISerializer &serialize( + /// @copybrief getComponentTypeInfos + const std::type_info &getComponentTypeInfos() const noexcept override final + { + return typeid(Component); + } + + /// @copydoc save + serialization::ISerializer &save( serialization::ISerializer &serializer, const std::type_info &stype, size_t entityId) const override final { if (contains(entityId)) - return serialization::serialize(serializer, stype, at(entityId)); + return serialization::save(serializer, stype, at(entityId)); return serializer; } + + /// @copydoc load + void load(serialization::ISerializer &serializer, const std::type_info &stype, size_t entityId) override final + { + if (contains(entityId)) + serialization::update(serializer, stype, at(entityId)); + else + serialization::load(serializer, stype, *this, entityId); + } + + /// + /// @brief Insert a new @b Component instance associated to the given entity. + /// + /// @param[in] index Index of the entity. + /// @param[in] c Component instance to insert. + /// + /// @return Component& Reference to the inserted component. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-25) + /// + virtual Component &insert(Entity::Index index, Component &&c) = 0; }; } // namespace ecstasy diff --git a/src/ecstasy/storages/IStorage.hpp b/src/ecstasy/storages/IStorage.hpp index 5a784040d..9dc69b821 100644 --- a/src/ecstasy/storages/IStorage.hpp +++ b/src/ecstasy/storages/IStorage.hpp @@ -85,7 +85,17 @@ namespace ecstasy virtual bool contains(size_t index) const noexcept = 0; /// - /// @brief Serialize an entity component. + /// @brief Get the Component stored type infos. + /// + /// @return const std::type_info& Type informations of the component stored in the storage. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-11) + /// + virtual const std::type_info &getComponentTypeInfos() const noexcept = 0; + + /// + /// @brief Save an entity component. /// /// @note The type_info is used to counter the impossibility to use template virtual methods. /// @@ -98,8 +108,20 @@ namespace ecstasy /// @author Andréas Leroux (andreas.leroux@epitech.eu) /// @since 1.0.0 (2024-06-11) /// - virtual serialization::ISerializer &serialize( + virtual serialization::ISerializer &save( serialization::ISerializer &serializer, const std::type_info &stype, size_t entityId) const = 0; + + /// + /// @brief Load/update an entity component. + /// + /// @param[in] serializer Serializer object. + /// @param[in] stype Type informations of the serializer. + /// @param[in] entityId Entity index. + /// + /// @author Andréas Leroux (andreas.leroux@epitech.eu) + /// @since 1.0.0 (2024-06-25) + /// + virtual void load(serialization::ISerializer &serializer, const std::type_info &stype, size_t entityId) = 0; }; } // namespace ecstasy diff --git a/src/ecstasy/storages/MapStorage.hpp b/src/ecstasy/storages/MapStorage.hpp index 75df5ee19..a7a42f7c1 100644 --- a/src/ecstasy/storages/MapStorage.hpp +++ b/src/ecstasy/storages/MapStorage.hpp @@ -72,6 +72,18 @@ namespace ecstasy return _components.emplace(std::make_pair(index, Component(std::forward(args)...))).first->second; } + /// @copydoc insert + Component &insert(Entity::Index index, Component &&c) override final + { + if constexpr (!std::movable) + throw std::runtime_error("MapStorage: Component is not movable"); + else { + _mask.resize(std::max(_mask.size(), index + 1)); + _mask[index] = true; + return _components.emplace(std::make_pair(index, std::move(c))).first->second; + } + } + /// @copydoc AStorage::erase(Entity::Index) bool erase(Entity::Index index) override final { diff --git a/src/ecstasy/storages/MarkerStorage.hpp b/src/ecstasy/storages/MarkerStorage.hpp index aa5a7d25a..ac6cd8c5d 100644 --- a/src/ecstasy/storages/MarkerStorage.hpp +++ b/src/ecstasy/storages/MarkerStorage.hpp @@ -75,6 +75,15 @@ namespace ecstasy return _defaultComponent; } + /// @copydoc insert + Component &insert(Entity::Index index, Component &&c) override final + { + (void)c; + _mask.resize(std::max(_mask.size(), index + 1)); + _mask[index] = true; + return _defaultComponent; + } + /// @copydoc AStorage::erase(Entity::Index) bool erase(Entity::Index index) override final { diff --git a/src/ecstasy/storages/VectorStorage.hpp b/src/ecstasy/storages/VectorStorage.hpp index c058ce81b..dfb1eb0f7 100644 --- a/src/ecstasy/storages/VectorStorage.hpp +++ b/src/ecstasy/storages/VectorStorage.hpp @@ -98,6 +98,28 @@ namespace ecstasy return _components[index]; } + /// @copydoc insert + Component &insert(Entity::Index index, Component &&c) override final + { + if constexpr (!std::movable) + throw std::runtime_error("VectorStorage: Component is not movable"); + else { + // Component at index already existing + if (_components.size() > index) + _components[index] = std::move(c); + else { + _mask.resize(std::max(_mask.size(), index + 1)); + // Padding elements + if (_components.size() < index) + _components.resize(index); + // New component + _components.push_back(std::move(c)); + } + _mask[index] = true; + return _components[index]; + } + } + /// @copydoc AStorage::erase /// /// @note Unset the flag in the mask but effectively delete the component only if it's the last one. Otherwise, diff --git a/tests/serialization/CMakeLists.txt b/tests/serialization/CMakeLists.txt index 845f38aa1..9d6ef80b6 100644 --- a/tests/serialization/CMakeLists.txt +++ b/tests/serialization/CMakeLists.txt @@ -1,6 +1,7 @@ set(SRCROOT ${SRCROOT}/serialization) set(SRC + ${SRCROOT}/tests_ComponentSerializer.cpp ${SRCROOT}/tests_concepts.cpp ${SRCROOT}/tests_Serializer.cpp ${SRCROOT}/tests_traits.cpp diff --git a/tests/serialization/tests_ComponentSerializer.cpp b/tests/serialization/tests_ComponentSerializer.cpp new file mode 100644 index 000000000..547f012e3 --- /dev/null +++ b/tests/serialization/tests_ComponentSerializer.cpp @@ -0,0 +1,89 @@ +#include +#include "ecstasy/registry/Registry.hpp" +#include "ecstasy/resources/entity/RegistryEntity.hpp" +#include "ecstasy/serialization/RawSerializer.hpp" +#include "ecstasy/serialization/Serializer.hpp" +#include "ecstasy/storages/MapStorage.hpp" + +using namespace ecstasy::serialization; + +struct Position { + public: + float x; + float y; + + Position(float px = 0.f, float py = 0.f) : x(px), y(py) + { + } + + Position(RawSerializer &serializer) : x(serializer.load()), y(serializer.load()) + { + } + + RawSerializer &operator>>(RawSerializer &serializer) const + { + return serializer.appendRaw(*this); + } + + Position &operator<<(RawSerializer &serializer) + { + serializer >> x >> y; + return *this; + } +}; + +struct NPC { + Position pos; + std::string name; + + using RawDTO = char *; + + NPC(Position p = Position(), std::string n = "") : pos(p), name(std::move(n)) + { + } + + NPC(RawSerializer &serializer) : pos(serializer), name(serializer.load()) + { + } + + RawSerializer &operator>>(RawSerializer &serializer) const + { + return serializer << pos << std::string_view(name); + } + + NPC &operator<<(RawSerializer &serializer) + { + serializer >> pos >> name; + return *this; + } +}; + +TEST(ComponentSerializer, SaveComponent) +{ + RawSerializer rawSerializer; + Position position(1.f, 2.f); + + rawSerializer << typeid(Position) << position; + std::string expected = rawSerializer.getStream().str(); + + rawSerializer.getStream().str(""); + ComponentSerializer::save(rawSerializer, typeid(RawSerializer), position); + GTEST_ASSERT_EQ(rawSerializer.str(), expected); +} + +TEST(ComponentSerializer, UpdateComponent) +{ + RawSerializer rawSerializer; + Position position(1.f, 2.f); + + // Don't include typeid here + rawSerializer << position; + std::string expected = rawSerializer.getStream().str(); + position.x = 420.f; + position.y = 69.f; + + rawSerializer.getStream().seekg(0); + ComponentSerializer::update(rawSerializer, typeid(RawSerializer), position); + GTEST_ASSERT_EQ(position.x, 1.f); + GTEST_ASSERT_EQ(position.y, 2.f); +} diff --git a/tests/serialization/tests_Serializer.cpp b/tests/serialization/tests_Serializer.cpp index 5df01496d..424e1358f 100644 --- a/tests/serialization/tests_Serializer.cpp +++ b/tests/serialization/tests_Serializer.cpp @@ -246,6 +246,7 @@ TEST(Serializer, entityComponents) registry.entityBuilder().with(1.0f, -8456.0f).with(Position(42.f, 0.f), "Steve").build(), registry); + //// Test saveEntity rawSerializer.saveEntity(entity); std::string entitySerializedExplicit = rawSerializer.getStream().str(); @@ -254,9 +255,30 @@ TEST(Serializer, entityComponents) std::string expected = rawSerializer.getStream().str(); GTEST_ASSERT_EQ(entitySerializedExplicit, expected); + /// Test Update entity + GTEST_ASSERT_EQ(entity.get().name, "Steve"); + entity.get().name = "John"; + + rawSerializer.getStream().seekg(0); + rawSerializer.updateEntity(entity); + GTEST_ASSERT_EQ(entity.get().name, "Steve"); + + /// Test loadEntity + rawSerializer.getStream().seekg(0); + ecstasy::RegistryEntity newEntity = rawSerializer.loadEntity(registry); + GTEST_ASSERT_EQ(newEntity.get().name, "Steve"); + + /// Final saveEntity test rawSerializer.getStream().str(""); rawSerializer.saveEntity(entity); std::string entitySerialized = rawSerializer.getStream().str(); + +#ifdef _MSC_VER + // MSVC does not guarantee the same order of the components + rawSerializer.getStream().str(""); + rawSerializer << typeid(Position) << entity.get() << typeid(NPC) << entity.get(); + expected = rawSerializer.getStream().str(); +#endif // Not a good test since the order of the components is not guaranteed GTEST_ASSERT_EQ(entitySerialized, expected); } diff --git a/tests/serialization/tests_concepts.cpp b/tests/serialization/tests_concepts.cpp index 265f9ac45..77b8b4152 100644 --- a/tests/serialization/tests_concepts.cpp +++ b/tests/serialization/tests_concepts.cpp @@ -52,10 +52,10 @@ TEST(is_serializer, all) TEST(has_extraction_operator, all) { - static_assert(concepts::has_extraction_operator, "False negative on RawSerializer"); - static_assert(!concepts::has_extraction_operator, + static_assert(concepts::has_extraction_operator, "False negative on RawSerializer"); + static_assert(!concepts::has_extraction_operator, "False positive on NonSerializable component"); - static_assert(!concepts::has_extraction_operator, "False positive on fundamental type (int)"); + static_assert(!concepts::has_extraction_operator, "False positive on fundamental type (int)"); } TEST(has_insertion_operator, all)