diff --git a/src/unrealsdk/unreal/cast.h b/src/unrealsdk/unreal/cast.h index 60d80e2..366baa7 100644 --- a/src/unrealsdk/unreal/cast.h +++ b/src/unrealsdk/unreal/cast.h @@ -35,9 +35,6 @@ namespace unrealsdk::unreal { * @note Intended for to be used to iterate over all types using recursive templates. */ using all_unreal_classes = std::tuple< // -#ifdef UE4 - UTextProperty, -#endif UArrayProperty, UBlueprintGeneratedClass, UBoolProperty, @@ -64,6 +61,7 @@ using all_unreal_classes = std::tuple< // UStrProperty, UStruct, UStructProperty, + UTextProperty, UUInt16Property, UUInt32Property, UUInt64Property, diff --git a/src/unrealsdk/unreal/classes/properties/utextproperty.cpp b/src/unrealsdk/unreal/classes/properties/utextproperty.cpp index 85e5415..6b650ca 100644 --- a/src/unrealsdk/unreal/classes/properties/utextproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/utextproperty.cpp @@ -7,8 +7,6 @@ #include "unrealsdk/unreal/wrappers/unreal_pointer.h" #include "unrealsdk/unrealsdk.h" -#ifdef UE4 - namespace unrealsdk::unreal { PropTraits::Value PropTraits::get( @@ -31,5 +29,3 @@ void PropTraits::destroy(const UTextProperty* /*prop*/, uintptr_t } } // namespace unrealsdk::unreal - -#endif diff --git a/src/unrealsdk/unreal/classes/properties/utextproperty.h b/src/unrealsdk/unreal/classes/properties/utextproperty.h index 0a964bc..a0ebaab 100644 --- a/src/unrealsdk/unreal/classes/properties/utextproperty.h +++ b/src/unrealsdk/unreal/classes/properties/utextproperty.h @@ -8,10 +8,6 @@ #include "unrealsdk/unreal/prop_traits.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" -// Unlike other properties, which we can define on engine versions which don't support them, text -// properties call into a game-specific hook, which means we can only define them in UE4 -#if defined(UE4) - namespace unrealsdk::unreal { #if defined(_MSC_VER) && defined(ARCH_X86) @@ -48,6 +44,4 @@ struct ClassTraits { } // namespace unrealsdk::unreal -#endif - #endif /* UNREALSDK_UNREAL_CLASSES_PROPERTIES_UTEXTPROPERTY_H */ diff --git a/src/unrealsdk/unreal/structs/ftext.cpp b/src/unrealsdk/unreal/structs/ftext.cpp index d90bdd0..073a596 100644 --- a/src/unrealsdk/unreal/structs/ftext.cpp +++ b/src/unrealsdk/unreal/structs/ftext.cpp @@ -5,20 +5,21 @@ #include "unrealsdk/unreal/structs/ftext.h" #include "unrealsdk/unrealsdk.h" #include "unrealsdk/utils.h" - -#ifdef UE4 +#include "unrealsdk/version_error.h" namespace unrealsdk::unreal { +#ifdef UE4 + FText::FText(const std::string& str) : FText(utils::widen(str)) {} FText::FText(const std::wstring& str) : data(), flags() { unrealsdk::ftext_as_culture_invariant(this, str); } -FText& FText::operator=(const std::string& str) noexcept { +FText& FText::operator=(const std::string& str) { return *this = utils::widen(str); } -FText& FText::operator=(const std::wstring& str) noexcept { +FText& FText::operator=(const std::wstring& str) { // Modifying in place is a pain, instead create a new FText and swap it in FText local_text{str}; std::swap(this->data, local_text.data); @@ -50,6 +51,43 @@ FText::~FText() { } } -} // namespace unrealsdk::unreal +#else + +FText::FText(const std::string& /* str */) : data(), flags() { + (void)this; + (void)this->data; + (void)this->flags; + throw_version_error("FTexts are not implemented in UE3"); +} +FText::FText(const std::wstring& /* str */) : data(), flags() { + (void)this; + throw_version_error("FTexts are not implemented in UE3"); +} +FText& FText::operator=(const std::string& /* str */) { + (void)this; + throw_version_error("FTexts are not implemented in UE3"); + return *this; +} +FText& FText::operator=(const std::wstring& /* str */) { + (void)this; + throw_version_error("FTexts are not implemented in UE3"); + return *this; +} +FText::operator std::string() const { + (void)this; + throw_version_error("FTexts are not implemented in UE3"); + return {}; +} +FText::operator std::wstring() const { + (void)this; + throw_version_error("FTexts are not implemented in UE3"); + return {}; +} +// Rather not throw from a destructor, since it will crash the whole game +FText::~FText() { + (void)this; +} #endif + +} // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/structs/ftext.h b/src/unrealsdk/unreal/structs/ftext.h index 037e1fb..0ff9000 100644 --- a/src/unrealsdk/unreal/structs/ftext.h +++ b/src/unrealsdk/unreal/structs/ftext.h @@ -41,8 +41,8 @@ struct FText { * @param str The string to assign. * @return A reference to this FText. */ - FText& operator=(const std::string& str) noexcept; - FText& operator=(const std::wstring& str) noexcept; + FText& operator=(const std::string& str); + FText& operator=(const std::wstring& str); /** * @brief Convert the FText to it's string representation. diff --git a/src/unrealsdk/unreal/wrappers/gobjects.cpp b/src/unrealsdk/unreal/wrappers/gobjects.cpp index 676fdb1..040980b 100644 --- a/src/unrealsdk/unreal/wrappers/gobjects.cpp +++ b/src/unrealsdk/unreal/wrappers/gobjects.cpp @@ -8,6 +8,7 @@ #include "unrealsdk/unreal/structs/gobjects.h" #else #include "unrealsdk/unreal/structs/tarray.h" +#include "unrealsdk/version_error.h" #endif namespace unrealsdk::unreal { @@ -133,12 +134,13 @@ UObject* GObjects::obj_at(size_t idx) const { UObject* GObjects::get_weak_object(const FWeakObjectPtr* /* ptr */) const { (void)this; - throw std::runtime_error("Weak object pointers are not implemented in UE3"); + throw_version_error("Weak object pointers are not implemented in UE3"); + return nullptr; } void GObjects::set_weak_object(FWeakObjectPtr* /* ptr */, const UObject* /* obj */) const { (void)this; - throw std::runtime_error("Weak object pointers are not implemented in UE3"); + throw_version_error("Weak object pointers are not implemented in UE3"); } #else diff --git a/src/unrealsdk/version_error.cpp b/src/unrealsdk/version_error.cpp new file mode 100644 index 0000000..4fe2d39 --- /dev/null +++ b/src/unrealsdk/version_error.cpp @@ -0,0 +1,18 @@ +#include "unrealsdk/pch.h" + +namespace unrealsdk { + +// Just noinline isn't enough for MSVC, also need to turn all optimizations off :/ +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__MINGW32__) +#pragma optimize("", off) +#endif + +void throw_version_error(const char* msg) { + throw std::runtime_error(msg); +} + +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__MINGW32__) +#pragma optimize("", on) +#endif + +} // namespace unrealsdk diff --git a/src/unrealsdk/version_error.h b/src/unrealsdk/version_error.h new file mode 100644 index 0000000..e9fe6c2 --- /dev/null +++ b/src/unrealsdk/version_error.h @@ -0,0 +1,44 @@ +#ifndef UNREALSDK_VERSION_ERROR_H +#define UNREALSDK_VERSION_ERROR_H + +#include "pch.h" + +/* +When attempting to use a feature which is not currently available to the sdk, it may throw a version +error. At this point, these should be caught using simply `std::exception`. A more detailed +exception may be exposed at a later date. +*/ + +namespace unrealsdk { + +/* +Since we sometimes expect version errors to be gated behind ifdefs, previously innocent user code +may sometimes unconditionally throw an error. If the exception gets inlined (including during LTO), +this may cause an unreachable code warning. While we want an unconditional exception, we don't want +the warning, there's nothing wrong with the user's code. + +To avoid this, hide throwing behind a noinline helper. +*/ + +#if defined(__clang__) || defined(__MINGW32__) +#define NOINLINE [[gnu::noinline]] +#elif defined(_MSC_VER) +#define NOINLINE [[msvc::noinline]] +#else +#error Unknown noinline attribute +#endif + +/** + * @brief Helper function to throw a version error. + * @note This function is (deliberately) not considered noreturn - you may still need to return a + * dummy value after calling it. + * + * @param msg The exception's message. + */ +NOINLINE void throw_version_error(const char* msg); + +#undef NOINLINE + +} // namespace unrealsdk + +#endif /* UNREALSDK_VERSION_ERROR_H */