#pragma once // Deliberately small UE-style surface used by the copied VoxelForge CPU sources. // This is a value/container compatibility layer; it does not start or link Unreal. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UCLASS(...) #define USTRUCT(...) #define UENUM(...) #define UPROPERTY(...) #define UFUNCTION(...) #define UMETA(...) #define GENERATED_BODY() #define GENERATED_UCLASS_BODY() #define VOXELFORGE_API #define FORCEINLINE inline #define WITH_EDITOR 0 #define TRACE_CPUPROFILER_EVENT_SCOPE(...) #define PRAGMA_DISABLE_SHADOW_VARIABLE_WARNINGS #define PRAGMA_ENABLE_SHADOW_VARIABLE_WARNINGS #define UE_ARRAY_COUNT(Array) (sizeof(Array) / sizeof((Array)[0])) #define TEXT(x) x #define UE_LOG(...) #define LogTemp 0 #define check(x) assert(x) #define checkf(x, ...) assert(x) #define ensure(x) (x) #define ensureMsgf(x, ...) (x) #define verify(x) (x) #define INDEX_NONE -1 #define MIN_int32 (std::numeric_limits::min()) #define MAX_int32 (std::numeric_limits::max()) #define MAX_uint32 (std::numeric_limits::max()) #define MIN_flt (-std::numeric_limits::max()) #define MAX_flt (std::numeric_limits::max()) #define PI 3.14159265358979323846f #define HALF_PI 1.57079632679489661923f #define SMALL_NUMBER 1.0e-8f #define KINDA_SMALL_NUMBER 1.0e-4f #define MoveTemp(x) std::move(x) using int8 = std::int8_t; using uint8 = std::uint8_t; using int16 = std::int16_t; using uint16 = std::uint16_t; using int32 = std::int32_t; using uint32 = std::uint32_t; using int64 = std::int64_t; using uint64 = std::uint64_t; using UPTRINT = std::uintptr_t; using TCHAR = char; using SIZE_T = std::size_t; constexpr uint64 MAX_uint64 = std::numeric_limits::max(); enum EForceInit { ForceInit }; enum class ESearchCase : uint8 { CaseSensitive, IgnoreCase }; struct FDefaultAllocator {}; template struct TInlineAllocator {}; enum class EAllowShrinking : uint8 { No, Yes }; template using TFunction = std::function; template using TFunctionRef = std::function; template constexpr T&& Forward(std::remove_reference_t& Value){return std::forward(Value);} template constexpr T&& Forward(std::remove_reference_t&& Value){return std::forward(Value);} struct FCompatAllocationHeader { std::pmr::memory_resource* Pool=nullptr; void* Base=nullptr; std::size_t Bytes=0; std::size_t Alignment=0; }; inline std::pmr::memory_resource* FCompatThreadPool() { // A synchronized pool can return a worker's allocations from the result-drain thread. Keep // one process-lifetime pool per allocating thread, matching Unreal's per-thread small bins. thread_local auto* Pool=new std::pmr::synchronized_pool_resource(); return Pool; } template class FCompatAllocator { public: using value_type=T; using is_always_equal=std::true_type; using propagate_on_container_move_assignment=std::true_type; FCompatAllocator() noexcept=default; template FCompatAllocator(const FCompatAllocator&) noexcept {} [[nodiscard]] T* allocate(std::size_t Count) { if(Count>std::numeric_limits::max()/sizeof(T))throw std::bad_array_new_length(); const std::size_t Bytes=Count*sizeof(T); const std::size_t Alignment=std::max(alignof(T),alignof(FCompatAllocationHeader)); const std::size_t Total=Bytes+sizeof(FCompatAllocationHeader)+Alignment; std::pmr::memory_resource* Pool=Bytes<=65536?FCompatThreadPool():nullptr; void* Base=Pool?Pool->allocate(Total,Alignment): ::operator new(Total,std::align_val_t(Alignment)); void* Candidate=static_cast(Base)+sizeof(FCompatAllocationHeader); std::size_t Space=Total-sizeof(FCompatAllocationHeader); void* Data=std::align(alignof(T),Bytes,Candidate,Space); if(!Data) { if(Pool)Pool->deallocate(Base,Total,Alignment); else ::operator delete(Base,std::align_val_t(Alignment)); throw std::bad_alloc(); } auto* Header=reinterpret_cast(static_cast(Data)-sizeof(FCompatAllocationHeader)); ::new (Header) FCompatAllocationHeader{Pool,Base,Total,Alignment}; return static_cast(Data); } void deallocate(T* Data,std::size_t) noexcept { if(!Data)return; auto* Header=reinterpret_cast(reinterpret_cast(Data)-sizeof(FCompatAllocationHeader)); const FCompatAllocationHeader Allocation=*Header; Header->~FCompatAllocationHeader(); if(Allocation.Pool)Allocation.Pool->deallocate(Allocation.Base,Allocation.Bytes,Allocation.Alignment); else ::operator delete(Allocation.Base,std::align_val_t(Allocation.Alignment)); } template bool operator==(const FCompatAllocator&)const noexcept{return true;} template bool operator!=(const FCompatAllocator&)const noexcept{return false;} }; template struct FCompatInlineCapacity { static constexpr std::size_t Value=0; }; template struct FCompatInlineCapacity> { static constexpr std::size_t Value=static_cast(N); }; template> class FCompatSmallVector { public: using value_type=T; using iterator=T*; using const_iterator=const T*; using reference=T&; using const_reference=const T&; FCompatSmallVector()=default; FCompatSmallVector(const FCompatSmallVector& Other){assign(Other.begin(),Other.end());} FCompatSmallVector(FCompatSmallVector&& Other) noexcept { if(Other.bHeap){Heap=std::move(Other.Heap);bHeap=true;Other.bHeap=false;Other.InlineSize=0;} else for(T& Value:Other)emplace_back(std::move(Value)); Other.clear(); } FCompatSmallVector& operator=(const FCompatSmallVector& Other) {if(this!=&Other)assign(Other.begin(),Other.end());return *this;} FCompatSmallVector& operator=(FCompatSmallVector&& Other) noexcept { if(this==&Other)return *this; clear(); if(Other.bHeap){Heap=std::move(Other.Heap);bHeap=true;Other.bHeap=false;Other.InlineSize=0;} else for(T& Value:Other)emplace_back(std::move(Value)); Other.clear();return *this; } ~FCompatSmallVector(){clear();} T* data(){return bHeap?Heap.data():InlineData();} const T* data()const{return bHeap?Heap.data():InlineData();} std::size_t size()const{return bHeap?Heap.size():InlineSize;} bool empty()const{return size()==0;} std::size_t capacity()const{return bHeap?Heap.capacity():InlineCount;} std::size_t heap_capacity()const{return bHeap?Heap.capacity():0;} iterator begin(){return data();} iterator end(){return data()+size();} const_iterator begin()const{return data();} const_iterator end()const{return data()+size();} reference operator[](std::size_t I){return data()[I];} const_reference operator[](std::size_t I)const{return data()[I];} reference back(){return data()[size()-1];} const_reference back()const{return data()[size()-1];} void reserve(std::size_t Count) { if(Count<=capacity())return; if(bHeap){Heap.reserve(Count);return;} std::vector NewHeap;NewHeap.reserve(Count); for(std::size_t I=0;ICount)InlineData()[--InlineSize].~T();} return; } reserve(Count); while(size() void assign(It First,It Last) {clear();for(;First!=Last;++First)emplace_back(*First);} void push_back(const T& Value){emplace_back(Value);} void push_back(T&& Value){emplace_back(std::move(Value));} template reference emplace_back(Args&&... ArgsIn) { if(bHeap){Heap.emplace_back(std::forward(ArgsIn)...);return Heap.back();} if(InlineSize(InlineData()+InlineSize)) T(std::forward(ArgsIn)...); ++InlineSize;return *Result; } reserve(std::max(InlineCount?InlineCount*2:1,InlineSize+1)); Heap.emplace_back(std::forward(ArgsIn)...);return Heap.back(); } void pop_back(){if(bHeap)Heap.pop_back();else InlineData()[--InlineSize].~T();} template iterator insert(iterator Position,It First,It Last) { const std::size_t Offset=static_cast(Position-begin()); assert(Offset==size()); for(;First!=Last;++First)emplace_back(*First); return begin()+Offset; } iterator erase(iterator First,iterator Last) { const std::size_t Start=static_cast(First-begin()); const std::size_t Finish=static_cast(Last-begin()); const std::size_t Old=size(); if(Finish<=Start)return begin()+Start; const std::size_t Removed=Finish-Start; for(std::size_t I=Finish;IOld-Removed)InlineData()[--InlineSize].~T(); return begin()+Start; } private: T* InlineData(){return reinterpret_cast(InlineBytes);} const T* InlineData()const{return reinterpret_cast(InlineBytes);} void DestroyInline()noexcept{while(InlineSize>0)InlineData()[--InlineSize].~T();} alignas(T) std::byte InlineBytes[(InlineCount?InlineCount:1)*sizeof(T)]{}; std::vector Heap; std::size_t InlineSize=0; bool bHeap=false; }; template class TArray { public: using value_type = T; using FStored = std::conditional_t,uint8,T>; using FStorage = FCompatSmallVector::Value,FCompatAllocator>; using iterator = typename FStorage::iterator; using const_iterator = typename FStorage::const_iterator; using reference = typename FStorage::reference; using const_reference = typename FStorage::const_reference; TArray() = default; TArray(std::initializer_list Values) : Values(Values) {} int32 Num() const { return static_cast(Values.size()); } bool IsEmpty() const { return Values.empty(); } bool IsValidIndex(int32 Index) const { return Index >= 0 && Index < Num(); } void Reserve(int32 Count) { Values.reserve(static_cast(std::max(0, Count))); } void Reset(int32 Slack = 0) { Values.clear(); if (Slack > 0) Reserve(Slack); } void Empty(int32 Slack = 0) { Reset(Slack); } void SetNum(int32 Count) { Values.resize(static_cast(std::max(0, Count))); } void Init(const T& Value,int32 Count) { Values.assign(static_cast(std::max(0,Count)),static_cast(Value)); } void SetNumZeroed(int32 Count) { SetNum(Count); if constexpr (std::is_trivially_copyable_v) if (!Values.empty()) std::memset(Values.data(), 0, Values.size() * sizeof(T)); } void SetNumUninitialized(int32 Count) { SetNum(Count); } int32 Add(const T& Value) { Values.push_back(Value); return Num() - 1; } int32 Add(T&& Value) { Values.push_back(std::move(Value)); return Num() - 1; } template int32 Emplace(Args&&... ArgsIn) { Values.emplace_back(std::forward(ArgsIn)...); return Num() - 1; } reference AddDefaulted_GetRef() { Values.emplace_back(); return Values.back(); } int32 AddDefaulted() { Values.emplace_back(); return Num() - 1; } template reference Emplace_GetRef(Args&&... ArgsIn) { Values.emplace_back(std::forward(ArgsIn)...); return Values.back(); } template reference Add_GetRef(Args&&... ArgsIn) { Values.emplace_back(std::forward(ArgsIn)...); return Values.back(); } int32 AddUnique(const T& Value) { if (Contains(Value)) return IndexOfByKey(Value); return Add(Value); } void Append(const TArray& Other) { Values.insert(Values.end(), Other.Values.begin(), Other.Values.end()); } void Append(TArray&& Other) { Values.insert(Values.end(), std::make_move_iterator(Other.Values.begin()), std::make_move_iterator(Other.Values.end())); } void RemoveAt(int32 Index, int32 Count = 1, bool = true) { if (Index >= 0 && Count > 0 && Index < Num()) Values.erase(Values.begin()+Index, Values.begin()+std::min(Num(),Index+Count)); } void RemoveAt(int32 Index, int32 Count, EAllowShrinking) { RemoveAt(Index, Count, true); } void RemoveAtSwap(int32 Index, int32 Count = 1, bool = true) { if (Index < 0 || Count <= 0 || Index >= Num()) return; const int32 End = std::min(Num(), Index + Count); for (int32 I = Index; I < End; ++I) Values[I] = std::move(Values.back()); Values.resize(Values.size() - static_cast(End - Index)); } void RemoveAtSwap(int32 Index, int32 Count, EAllowShrinking) { RemoveAtSwap(Index, Count, true); } template int32 RemoveAll(Pred P) { const auto Before=Values.size(); Values.erase(std::remove_if(Values.begin(),Values.end(),P),Values.end()); return static_cast(Before-Values.size()); } bool Contains(const T& Value) const { return std::find(Values.begin(), Values.end(), Value) != Values.end(); } int32 IndexOfByKey(const T& Value) const { auto I=std::find(Values.begin(),Values.end(),Value); return I==Values.end()?INDEX_NONE:static_cast(I-Values.begin()); } T* Find(const T& Value) { const int32 I=IndexOfByKey(Value); return I==INDEX_NONE?nullptr:&Values[I]; } const T* Find(const T& Value) const { const int32 I=IndexOfByKey(Value); return I==INDEX_NONE?nullptr:&Values[I]; } T* GetData() { return reinterpret_cast(Values.data()); } const T* GetData() const { return reinterpret_cast(Values.data()); } std::size_t GetAllocatedSize() const { return Values.heap_capacity() * sizeof(T); } T& Last() { return Values.back(); } const T& Last() const { return Values.back(); } T Pop(bool = false) { T Out=std::move(Values.back()); Values.pop_back(); return Out; } void Swap(int32 A,int32 B) { std::swap(Values[A],Values[B]); } template void Sort(Pred P) { std::sort(Values.begin(),Values.end(),P); } void Sort() { std::sort(Values.begin(),Values.end()); } reference operator[](int32 I) { return Values[static_cast(I)]; } const_reference operator[](int32 I) const { return Values[static_cast(I)]; } iterator begin() { return Values.begin(); } iterator end() { return Values.end(); } const_iterator begin() const { return Values.begin(); } const_iterator end() const { return Values.end(); } const_iterator cbegin() const { return Values.cbegin(); } const_iterator cend() const { return Values.cend(); } private: FStorage Values; }; template struct TPair { K Key{}; V Value{}; TPair()=default; TPair(const K& K0,const V& V0):Key(K0),Value(V0){}; }; template struct FCompatKeyHash { std::size_t operator()(const K& Key) const noexcept { if constexpr (std::is_integral_v || std::is_enum_v) return std::hash{}(Key); else if constexpr (requires { GetTypeHash(Key); }) return static_cast(GetTypeHash(Key)); else static_assert(sizeof(K) == 0, "Standalone TMap/TSet key type needs GetTypeHash"); } }; template class TMap { public: int32 Num() const { return static_cast(Pairs.size()); } bool IsEmpty() const { return Pairs.empty(); } void Reserve(int32 N) { const size_t Count=static_cast(std::max(0,N)); Pairs.reserve(Count); const size_t Needed=std::max(8,std::bit_ceil(std::max(1,(Count*10+6)/7))); if(Buckets.size()(I)].Value; } const V* Find(const K& Key) const { const int32 I=FindIndex(Key);return I==INDEX_NONE?nullptr:&Pairs[static_cast(I)].Value; } V& FindOrAdd(const K& Key) { if (V* P=Find(Key)) return *P; return Add(Key,V{}); } V& Add(const K& Key,const V& Value) { if (V* P=Find(Key)) { *P=Value; return *P; } PrepareInsert();const int32 I=static_cast(Pairs.size());Pairs.emplace_back(Key,Value);InsertIndex(Key,I);return Pairs.back().Value; } V& Add(const K& Key,V&& Value) { if (V* P=Find(Key)) { *P=std::move(Value); return *P; } PrepareInsert();const int32 I=static_cast(Pairs.size());Pairs.emplace_back(Key,std::move(Value));InsertIndex(Key,I);return Pairs.back().Value; } int32 Remove(const K& Key) { const int32 I=FindIndex(Key);if(I==INDEX_NONE)return 0; MarkRemoved(Key);const int32 Last=static_cast(Pairs.size()-1); if(I!=Last){Pairs[static_cast(I)]=std::move(Pairs.back());UpdateIndex(Pairs[static_cast(I)].Key,I);} Pairs.pop_back(); if(Tombstones>Pairs.size()&&Tombstones>Buckets.size()/8)RebuildIndex(Buckets.size()); return 1; } V& operator[](const K& Key) { return FindOrAdd(Key); } const V& operator[](const K& Key) const { const V* P=Find(Key); assert(P); return *P; } auto begin(){return Pairs.begin();} auto end(){return Pairs.end();} auto begin()const{return Pairs.begin();} auto end()const{return Pairs.end();} private: struct FBucket { uint32 Generation=0;int32 Index=INDEX_NONE; }; void PrepareInsert() { const size_t Count=Pairs.size()+1; const size_t Needed=std::max(8,std::bit_ceil(std::max(1,(Count*10+6)/7))); if(Buckets.size()Buckets.size()/8)RebuildIndex(std::max(Needed,Buckets.size())); } int32 FindIndex(const K& Key)const { if(Buckets.empty())return INDEX_NONE; size_t Slot=FCompatKeyHash{}(Key)&(Buckets.size()-1); for(size_t Probe=0;Probe=0&&Pairs[static_cast(B.Index)].Key==Key)return B.Index; Slot=(Slot+1)&(Buckets.size()-1); } return INDEX_NONE; } void InsertIndex(const K& Key,int32 PairIndex) { size_t Slot=FCompatKeyHash{}(Key)&(Buckets.size()-1),FirstTombstone=Buckets.size(); for(size_t Probe=0;Probe{}(Key)&(Buckets.size()-1); for(size_t Probe=0;Probe=0&&Pairs[static_cast(B.Index)].Key==Key){B.Index=INDEX_NONE;++Tombstones;return;} Slot=(Slot+1)&(Buckets.size()-1); } } void UpdateIndex(const K& Key,int32 PairIndex) { size_t Slot=FCompatKeyHash{}(Key)&(Buckets.size()-1); for(size_t Probe=0;Probe=0&&Pairs[static_cast(B.Index)].Key==Key){B.Index=PairIndex;return;} Slot=(Slot+1)&(Buckets.size()-1); } } void RebuildIndex(size_t Capacity) { Capacity=std::max(8,std::bit_ceil(Capacity));Buckets.assign(Capacity,FBucket{});Generation=1;Tombstones=0; for(int32 I=0;I(Pairs.size());++I)InsertIndex(Pairs[static_cast(I)].Key,I); } std::vector,FCompatAllocator>> Pairs; std::vector> Buckets; uint32 Generation=1; size_t Tombstones=0; }; template class TSet { public: int32 Num()const{return Values.Num();} void Reset(){Values.Reset();Indices.Reset();} void Empty(){Reset();} void Reserve(int32 N){Values.Reserve(N);Indices.Reserve(N);} bool Contains(const T& V)const{return Indices.Contains(V);} void Add(const T& V){if(!Indices.Contains(V)){const int32 I=Values.Add(V);Indices.Add(V,I);}} void Add(T&& V){if(!Indices.Contains(V)){const int32 I=Values.Add(std::move(V));Indices.Add(Values[I],I);}} auto begin(){return Values.begin();}auto end(){return Values.end();}auto begin()const{return Values.begin();}auto end()const{return Values.end();} private:TArray Values;TMap Indices; }; template class TUniquePtr { public: TUniquePtr()=default; TUniquePtr(std::nullptr_t){} explicit TUniquePtr(std::unique_ptr&&P):Ptr(std::move(P)){} template requires std::is_convertible_v TUniquePtr(TUniquePtr&& P):Ptr(P.Release()){} TUniquePtr(TUniquePtr&&)=default; TUniquePtr& operator=(TUniquePtr&&)=default; TUniquePtr(const TUniquePtr&)=delete; TUniquePtr& operator=(const TUniquePtr&)=delete; template requires std::is_convertible_v TUniquePtr& operator=(TUniquePtr&& P){Ptr.reset(P.Release());return *this;} T* Get()const{return Ptr.get();} T* get()const{return Ptr.get();} T* operator->()const{return Ptr.get();} T& operator*()const{return *Ptr;} explicit operator bool()const{return !!Ptr;} bool IsValid()const{return !!Ptr;} void Reset(T*P=nullptr){Ptr.reset(P);} void reset(T*P=nullptr){Ptr.reset(P);} T* Release(){return Ptr.release();} void Swap(TUniquePtr&O){Ptr.swap(O.Ptr);} private:std::unique_ptr Ptr; }; enum class ESPMode : uint8 { NotThreadSafe, ThreadSafe }; template class TSharedPtr { public:TSharedPtr()=default;TSharedPtr(std::nullptr_t){}TSharedPtr(std::shared_ptrP):Ptr(std::move(P)){}template requires std::is_convertible_v TSharedPtr(const TSharedPtr&P):Ptr(P.Std()){}T*Get()const{return Ptr.get();}T*operator->()const{return Ptr.get();}T&operator*()const{return *Ptr;}explicit operator bool()const{return !!Ptr;}bool IsValid()const{return !!Ptr;}void Reset(){Ptr.reset();}void Reset(T*P){Ptr.reset(P);}std::shared_ptr&Std(){return Ptr;}const std::shared_ptr&Std()const{return Ptr;}friend bool operator==(const TSharedPtr&P,std::nullptr_t){return !P.Ptr;}friend bool operator!=(const TSharedPtr&P,std::nullptr_t){return !!P.Ptr;}private:std::shared_ptrPtr; }; template using TSharedRef = TSharedPtr; template class TOptional { public:TOptional()=default;TOptional(const T&V):Value(V){}TOptional(T&&V):Value(std::move(V)){}bool IsSet()const{return Value.has_value();}explicit operator bool()const{return Value.has_value();}T&GetValue(){return Value.value();}const T&GetValue()const{return Value.value();}T&Get(){return Value.value();}const T&Get()const{return Value.value();}void Reset(){Value.reset();}private:std::optionalValue;}; template TUniquePtr MakeUnique(A&&... Args) { return TUniquePtr(std::make_unique(std::forward(Args)...)); } template TSharedRef MakeShared(A&&... Args) { return TSharedRef(std::make_shared(std::forward(Args)...)); } class FString : public std::string { public: using std::string::string; FString()=default; FString(const std::string& S):std::string(S){} FString(std::string&& S):std::string(std::move(S)){} bool IsEmpty()const{return empty();} void Reset(){clear();} int32 Len()const{return static_cast(size());} bool Contains(std::string_view S)const{return find(S)!=npos;} bool StartsWith(std::string_view S)const{return rfind(S,0)==0;} bool EndsWith(std::string_view S)const{return size()>=S.size()&&compare(size()-S.size(),S.size(),S)==0;} bool Equals(std::string_view S, ESearchCase SearchCase = ESearchCase::CaseSensitive)const{if(SearchCase==ESearchCase::CaseSensitive)return *this==S;if(size()!=S.size())return false;for(size_t I=0;I((*this)[I]))!=std::tolower(static_cast(S[I])))return false;return true;} void ToLowerInline(){for(char& C:*this)C=static_cast(std::tolower(static_cast(C)));} void TrimStartAndEndInline(){auto B=find_first_not_of(" \t\r\n"),E=find_last_not_of(" \t\r\n");*this=(B==npos)?FString{}:substr(B,E-B+1);} void LeftChopInline(std::size_t N){if(N>=size())clear();else resize(size()-N);} int32 ReplaceInline(std::string_view From,std::string_view To){if(From.empty())return 0;int32 C=0;std::size_t P=0;while((P=find(From,P))!=npos){replace(P,From.size(),To);P+=To.size();++C;}return C;} static FString Join(const TArray& Parts, std::string_view Delimiter){FString Result;for(int32 I=0;I(std::min(N,sizeof(Buf)-1)));} }; inline uint32 GetTypeHash(const FString& Value) { const std::string_view View(Value.data(),Value.size()); return static_cast(std::hash{}(View)); } inline const char* operator*(const FString& S){return S.c_str();} using FName = FString; constexpr const char* NAME_None = ""; class UObject; template T* FindStandaloneAsset(const FString& Path); void RegisterStandaloneAsset(const FString& Path, UObject* Asset); class FText { FString Value; public: FText()=default; FText(FString S):Value(std::move(S)){} static FText FromString(const FString& S){return FText(S);} static FText FromName(const FName& S){return FText(S);} FString ToString()const{return Value;} bool IsEmpty()const{return Value.IsEmpty();} }; class UEnum { public: FString GetNameStringByValue(int64 V)const{return FString::Printf("Value_%lld",static_cast(V));} }; template const UEnum* StaticEnum(){static const UEnum Value;return &Value;} constexpr uint32 RF_Transient = 1; template T* NewObject(UObject* = nullptr,FName = NAME_None,uint32 = 0){return new T;} template T* DuplicateObject(const T* Source,UObject*){return Source?new T(*Source):nullptr;} template class TGuardValue { public:TGuardValue(T& Target,const T& NewValue):Ref(Target),Old(Target){Ref=NewValue;}~TGuardValue(){Ref=Old;}TGuardValue(const TGuardValue&)=delete;private:T&Ref;T Old;}; struct FIntPoint { int32 X=0,Y=0; constexpr FIntPoint()=default; constexpr FIntPoint(int32 A,int32 B):X(A),Y(B){} static const FIntPoint ZeroValue; bool operator==(const FIntPoint&O)const{return X==O.X&&Y==O.Y;} bool operator!=(const FIntPoint&O)const{return !(*this==O);} FIntPoint operator+(const FIntPoint&O)const{return {X+O.X,Y+O.Y};} FIntPoint operator-(const FIntPoint&O)const{return {X-O.X,Y-O.Y};} }; inline const FIntPoint FIntPoint::ZeroValue{0,0}; struct FIntVector { int32 X=0,Y=0,Z=0; constexpr FIntVector()=default; constexpr FIntVector(int32 A,int32 B,int32 C):X(A),Y(B),Z(C){} static const FIntVector ZeroValue; bool operator==(const FIntVector&O)const{return X==O.X&&Y==O.Y&&Z==O.Z;} bool operator!=(const FIntVector&O)const{return !(*this==O);} FIntVector operator+(const FIntVector&O)const{return {X+O.X,Y+O.Y,Z+O.Z};} FIntVector operator-(const FIntVector&O)const{return {X-O.X,Y-O.Y,Z-O.Z};} FIntVector operator*(int32 S)const{return {X*S,Y*S,Z*S};} FIntVector operator/(int32 S)const{return {X/S,Y/S,Z/S};} FIntVector& operator+=(const FIntVector&O){X+=O.X;Y+=O.Y;Z+=O.Z;return *this;} }; inline const FIntVector FIntVector::ZeroValue{0,0,0}; inline FIntVector operator*(int32 S,const FIntVector& V){return V*S;} struct FVector2D { double X=0,Y=0; constexpr FVector2D()=default; constexpr FVector2D(double A,double B):X(A),Y(B){} static const FVector2D ZeroVector; double SizeSquared()const{return X*X+Y*Y;} double Size()const{return std::sqrt(SizeSquared());} bool Normalize(double Tol=1.e-8){double L=Size();if(L<=Tol)return false;X/=L;Y/=L;return true;} FVector2D GetSafeNormal(double Tol=1.e-8)const{FVector2D V=*this;V.Normalize(Tol);return V;} double DistSquared(const FVector2D&O)const{return (*this-O).SizeSquared();} static double DistSquared(const FVector2D&A,const FVector2D&B){return (A-B).SizeSquared();} FVector2D operator-()const{return {-X,-Y};} FVector2D operator+(const FVector2D&O)const{return {X+O.X,Y+O.Y};} FVector2D operator-(const FVector2D&O)const{return {X-O.X,Y-O.Y};} FVector2D operator*(double S)const{return {X*S,Y*S};} FVector2D operator/(double S)const{return {X/S,Y/S};} FVector2D&operator+=(const FVector2D&O){X+=O.X;Y+=O.Y;return *this;} FVector2D&operator*=(double S){X*=S;Y*=S;return *this;} static double DotProduct(const FVector2D&A,const FVector2D&B){return A.X*B.X+A.Y*B.Y;} }; inline const FVector2D FVector2D::ZeroVector{0,0}; struct FVector2f { float X=0,Y=0; constexpr FVector2f()=default; constexpr FVector2f(float A,float B):X(A),Y(B){} }; struct FVector3f { float X=0,Y=0,Z=0; constexpr FVector3f()=default; constexpr FVector3f(float A,float B,float C):X(A),Y(B),Z(C){} FVector3f operator+(const FVector3f&O)const{return {X+O.X,Y+O.Y,Z+O.Z};} FVector3f operator-(const FVector3f&O)const{return {X-O.X,Y-O.Y,Z-O.Z};} FVector3f operator*(float S)const{return {X*S,Y*S,Z*S};} FVector3f&operator+=(const FVector3f&O){X+=O.X;Y+=O.Y;Z+=O.Z;return *this;} bool operator==(const FVector3f&O)const{return X==O.X&&Y==O.Y&&Z==O.Z;} }; struct FVector { using FReal = double; double X=0,Y=0,Z=0; constexpr FVector()=default; constexpr explicit FVector(double S):X(S),Y(S),Z(S){} constexpr FVector(double A,double B,double C):X(A),Y(B),Z(C){} constexpr explicit FVector(const FIntVector& V):X(V.X),Y(V.Y),Z(V.Z){} static const FVector ZeroVector, OneVector, UpVector, DownVector, ForwardVector, RightVector; FVector operator+(const FVector&O)const{return {X+O.X,Y+O.Y,Z+O.Z};} FVector operator-(const FVector&O)const{return {X-O.X,Y-O.Y,Z-O.Z};} FVector operator-()const{return {-X,-Y,-Z};} FVector operator*(double S)const{return {X*S,Y*S,Z*S};} FVector operator/(double S)const{return {X/S,Y/S,Z/S};} FVector operator/(const FVector&O)const{return {X/O.X,Y/O.Y,Z/O.Z};} FVector& operator+=(const FVector&O){X+=O.X;Y+=O.Y;Z+=O.Z;return *this;} FVector& operator-=(const FVector&O){X-=O.X;Y-=O.Y;Z-=O.Z;return *this;} FVector& operator*=(double S){X*=S;Y*=S;Z*=S;return *this;} FVector& operator/=(double S){X/=S;Y/=S;Z/=S;return *this;} bool operator==(const FVector&O)const{return X==O.X&&Y==O.Y&&Z==O.Z;} bool operator!=(const FVector&O)const{return !(*this==O);} double SizeSquared()const{return X*X+Y*Y+Z*Z;} double Size()const{return std::sqrt(SizeSquared());} double Dist(const FVector&O)const{return (*this-O).Size();} double DistSquared(const FVector&O)const{return (*this-O).SizeSquared();} static double Dist(const FVector&A,const FVector&B){return (A-B).Size();} static double DistSquared(const FVector&A,const FVector&B){return (A-B).SizeSquared();} bool Normalize(double Tolerance=1.e-8){double L=Size();if(L<=Tolerance)return false;*this=*this/L;return true;} FVector GetAbs()const{return {std::abs(X),std::abs(Y),std::abs(Z)};} FVector GetSafeNormal(double Tolerance=1.e-8)const{FVector V=*this;V.Normalize(Tolerance);return V;} bool IsNearlyZero(double Tolerance=1.e-8)const{return SizeSquared()<=Tolerance*Tolerance;} bool ContainsNaN()const{return !std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z);} bool Equals(const FVector&O,double Tolerance=1.e-8)const{return (*this-O).IsNearlyZero(Tolerance);} double Dot(const FVector&O)const{return X*O.X+Y*O.Y+Z*O.Z;} FVector Cross(const FVector&O)const{return {Y*O.Z-Z*O.Y,Z*O.X-X*O.Z,X*O.Y-Y*O.X};} static double DotProduct(const FVector&A,const FVector&B){return A.Dot(B);} static FVector CrossProduct(const FVector&A,const FVector&B){return A.Cross(B);} }; inline const FVector FVector::ZeroVector{0,0,0}; inline const FVector FVector::OneVector{1,1,1}; inline const FVector FVector::UpVector{0,0,1}; inline const FVector FVector::DownVector{0,0,-1}; inline const FVector FVector::ForwardVector{1,0,0}; inline const FVector FVector::RightVector{0,1,0}; inline FVector operator*(double S,const FVector&V){return V*S;} inline FVector operator*(float S,const FVector&V){return V*static_cast(S);} struct FBox { FVector Min{MAX_flt,MAX_flt,MAX_flt},Max{MIN_flt,MIN_flt,MIN_flt}; bool IsValid=false; FBox()=default; explicit FBox(EForceInit):FBox(){} FBox(const FVector&A,const FVector&B):Min(A),Max(B),IsValid(A.X<=B.X&&A.Y<=B.Y&&A.Z<=B.Z){} FVector GetCenter()const{return (Min+Max)*0.5;} FVector GetExtent()const{return (Max-Min)*0.5;} bool IsInside(const FVector&P)const{return P.X>=Min.X&&P.X<=Max.X&&P.Y>=Min.Y&&P.Y<=Max.Y&&P.Z>=Min.Z&&P.Z<=Max.Z;} bool operator==(const FBox&O)const{return Min==O.Min&&Max==O.Max&&IsValid==O.IsValid;} bool operator!=(const FBox&O)const{return !(*this==O);} }; struct FColor { uint8 R=0,G=0,B=0,A=255; constexpr FColor()=default; constexpr FColor(uint8 r,uint8 g,uint8 b,uint8 a=255):R(r),G(g),B(b),A(a){} static const FColor White, Black, Red, Green, Blue, Transparent; }; inline const FColor FColor::White{255,255,255,255}; inline const FColor FColor::Black{0,0,0,255}; inline const FColor FColor::Red{255,0,0,255}; inline const FColor FColor::Green{0,255,0,255}; inline const FColor FColor::Blue{0,0,255,255}; inline const FColor FColor::Transparent{0,0,0,0}; struct FLinearColor { float R=0,G=0,B=0,A=1; constexpr FLinearColor()=default; constexpr FLinearColor(float r,float g,float b,float a=1):R(r),G(g),B(b),A(a){} static const FLinearColor White,Black,Red,Green,Blue; FColor ToFColor(bool bSRGB)const{auto cv=[](float V,bool S){V=std::clamp(V,0.0f,1.0f);if(S)V=V<=0.0031308f?12.92f*V:1.055f*std::pow(V,1.0f/2.4f)-0.055f;return static_cast(std::clamp(static_cast(std::floor(V*255.0f+0.5f)),0,255));};return {cv(R,bSRGB),cv(G,bSRGB),cv(B,bSRGB),cv(A,false)};} }; inline const FLinearColor FLinearColor::White{1,1,1,1}; inline const FLinearColor FLinearColor::Black{0,0,0,1}; inline const FLinearColor FLinearColor::Red{1,0,0,1}; inline const FLinearColor FLinearColor::Green{0,1,0,1}; inline const FLinearColor FLinearColor::Blue{0,0,1,1}; struct FRotator { double Pitch=0,Yaw=0,Roll=0; constexpr FRotator()=default; constexpr FRotator(double P,double Y,double R):Pitch(P),Yaw(Y),Roll(R){} static const FRotator ZeroRotator; }; inline const FRotator FRotator::ZeroRotator{0,0,0}; namespace FMath { template constexpr auto Min(A A0,B B0){using R=std::common_type_t;R A1=static_cast(A0),B1=static_cast(B0);return A1 constexpr auto Max(A A0,B B0){using R=std::common_type_t;R A1=static_cast(A0),B1=static_cast(B0);return A1>B1?A1:B1;} template constexpr T Clamp(T V,A A0,B B0){T Lo=static_cast(A0),Hi=static_cast(B0);return VHi?Hi:V);} template constexpr T Abs(T V){return V(std::floor(V));} inline int32 CeilToInt(double V){return static_cast(std::ceil(V));} inline int32 RoundToInt(double V){return static_cast(std::floor(V+0.5));} inline double FloorToDouble(double V){return std::floor(V);} inline double CeilToDouble(double V){return std::ceil(V);} inline double RoundToDouble(double V){return std::floor(V+0.5);} inline float FloorToFloat(float V){return std::floor(V);} inline float CeilToFloat(float V){return std::ceil(V);} inline float RoundToFloat(float V){return std::floor(V+0.5f);} inline bool IsFinite(double V){return std::isfinite(V);} inline bool IsNearlyZero(double V,double T=1.e-8){return std::abs(V)<=T;} inline bool IsNearlyEqual(double A,double B,double T=1.e-8){return std::abs(A-B)<=T;} inline float Sqrt(float V){return std::sqrt(V);} inline double Sqrt(double V){return std::sqrt(V);} inline float InvSqrt(float V){return 1.0f/std::sqrt(V);} inline double InvSqrt(double V){return 1.0/std::sqrt(V);} inline float Sin(float V){return std::sin(V);} inline double Sin(double V){return std::sin(V);} inline float Cos(float V){return std::cos(V);} inline double Cos(double V){return std::cos(V);} inline float Tan(float V){return std::tan(V);} inline double Tan(double V){return std::tan(V);} inline float Asin(float V){return std::asin(V);} inline double Asin(double V){return std::asin(V);} inline float Acos(float V){return std::acos(V);} inline double Acos(double V){return std::acos(V);} inline float Atan2(float Y,float X){return std::atan2(Y,X);} inline double Atan2(double Y,double X){return std::atan2(Y,X);} inline float Pow(float A,float B){return std::pow(A,B);} inline double Pow(double A,double B){return std::pow(A,B);} inline float Exp(float V){return std::exp(V);} inline double Exp(double V){return std::exp(V);} inline float Loge(float V){return std::log(V);} inline float Fmod(float A,float B){return std::fmod(A,B);} inline double Fmod(double A,double B){return std::fmod(A,B);} template constexpr T Square(T V){return V*V;} template constexpr T Sign(T V){return VT(0)?T(1):T(0));} template constexpr T Max3(T A,T B,T C){return Max(Max(A,B),C);} template constexpr T Min3(T A,T B,T C){return Min(Min(A,B),C);} inline double DegreesToRadians(double D){return D*(3.1415926535897932384626433832795/180.0);} inline double RadiansToDegrees(double R){return R*(180.0/3.1415926535897932384626433832795);} inline float Frac(float V){return V-std::floor(V);} inline double Frac(double V){return V-std::floor(V);} inline int32 DivideAndRoundUp(int32 A,int32 B){return (A+B-1)/B;} template bool IsPowerOfTwo(T V){return V>0&&(V&(V-1))==0;} inline int32 FloorLog2(uint32 V){return V?31-std::countl_zero(V):0;} float PerlinNoise2D(const FVector2D& Location); float PerlinNoise3D(const FVector& Location); template inline auto Lerp(const A& A0,const B& B0,C Alpha)->decltype(A0+(B0-A0)*Alpha){return A0+(B0-A0)*Alpha;} inline float SmoothStep(float A,float B,float X){float T=Clamp((X-A)/(B-A),0.0f,1.0f);return T*T*(3-2*T);} } inline uint32 HashCombine(uint32 A,uint32 B){return A^(B+0x9e3779b9u+(A<<6)+(A>>2));} inline uint32 GetTypeHash(int32 V){return static_cast(V)*0x9e3779b9u;} inline uint32 GetTypeHash(uint32 V){return V*0x9e3779b9u;} inline uint32 GetTypeHash(float V){return std::bit_cast(V);} inline uint32 GetTypeHash(double V){return static_cast(std::bit_cast(V)^ (std::bit_cast(V)>>32));} inline uint32 GetTypeHash(int64 V){return static_cast(static_cast(V)^(static_cast(V)>>32));} inline uint32 GetTypeHash(uint64 V){return static_cast(V^(V>>32));} inline uint32 GetTypeHash(const FIntVector& V){return HashCombine(HashCombine(GetTypeHash(V.X),GetTypeHash(V.Y)),GetTypeHash(V.Z));} inline uint32 GetTypeHash(const FIntPoint& V){return HashCombine(GetTypeHash(V.X),GetTypeHash(V.Y));} struct FMemory { static void Memcpy(void*D,const void*S,std::size_t N){std::memcpy(D,S,N);} static void Memzero(void*D,std::size_t N){std::memset(D,0,N);} static int32 Memcmp(const void*A,const void*B,std::size_t N){return std::memcmp(A,B,N);} static SIZE_T GetAllocSize(const void* P){return P?sizeof(void*):0;} }; struct FPrimaryAssetId { FString Type,Name; FPrimaryAssetId()=default; FPrimaryAssetId(FString T,FName N):Type(std::move(T)),Name(std::move(N)){} }; class UObject { public: virtual ~UObject()=default; virtual void PostLoad(){} virtual FName GetFName()const{return FName{};} virtual FString GetPathName()const{return FString{};} virtual bool Modify(){return true;} virtual void MarkPackageDirty(){} }; inline std::unordered_map& StandaloneAssetRegistry() { static std::unordered_map Assets; return Assets; } inline void RegisterStandaloneAsset(const FString& Path, UObject* Asset) { if (!Path.IsEmpty() && Asset) StandaloneAssetRegistry()[Path] = Asset; } template inline T* FindStandaloneAsset(const FString& Path) { auto It = StandaloneAssetRegistry().find(Path); return It == StandaloneAssetRegistry().end() ? nullptr : dynamic_cast(It->second); } class UDataAsset : public UObject {}; class UPrimaryDataAsset : public UDataAsset { public: virtual FPrimaryAssetId GetPrimaryAssetId()const{return {};} }; using FSoftObjectPath = FString; template class TSoftObjectPtr { public: TSoftObjectPtr()=default; TSoftObjectPtr(T*P):Ptr(P){} TSoftObjectPtr(const FSoftObjectPath&P):Path(P){} bool IsNull()const{return Ptr==nullptr&&Path.IsEmpty();} T* Get()const{return Ptr?Ptr:FindStandaloneAsset(Path);} T* LoadSynchronous()const{return Get();} FString ToString()const{return Path;} void Set(T*P,FString S={}){Ptr=P;Path=std::move(S);} T* operator->()const{return Get();} explicit operator bool()const{return Get()!=nullptr;} private:T*Ptr=nullptr;FString Path; }; template class TWeakObjectPtr { public:TWeakObjectPtr()=default;TWeakObjectPtr(T*P):Ptr(P){}T*Get()const{return Ptr;}T*operator->()const{return Ptr;}private:T*Ptr=nullptr;}; template class TSubclassOf { public:TSubclassOf()=default; }; class FGameplayTag {}; class FGameplayTagContainer {}; struct FRandomStream { explicit FRandomStream(int32 S=0):Seed(S){} int32 RandRange(int32 Min,int32 Max){Seed=Seed*196314165+907633515;uint32 R=(Seed>>16)&0x7fff;return Min+static_cast((uint64(R)*(uint64(Max-Min)+1))>>15);} int32 GetCurrentSeed()const{return Seed;} private:int32 Seed; }; struct FCString { static int32 Strcmp(const char*A,const char*B){return std::strcmp(A,B);} static float Atof(const char*S){return static_cast(std::atof(S));} static int32 Atoi(const char*S){return std::atoi(S);} }; struct FPlatformTime { using Clock=std::chrono::steady_clock; static uint64 Cycles64(){return static_cast(std::chrono::duration_cast(Clock::now().time_since_epoch()).count());} static double Seconds(){return Cycles64()*1e-9;} static double GetSecondsPerCycle64(){return 1e-9;} }; struct FEvent { void Trigger(){} bool Wait(uint32=0,bool=false){return true;} }; struct FPlatformProcess { static void Sleep(float S){std::this_thread::sleep_for(std::chrono::duration(S));} static void YieldThread(){std::this_thread::yield();} static void Yield(){std::this_thread::yield();} static FEvent* GetSynchEventFromPool(bool=false){return new FEvent;} static void ReturnSynchEventToPool(FEvent*E){delete E;} }; using FCriticalSection = std::mutex; struct FScopeLock { explicit FScopeLock(std::mutex&M):Lock(M){} explicit FScopeLock(std::mutex* M):Lock(*M){} std::lock_guard Lock; }; struct FRWLock { mutable std::shared_mutex Mutex; }; struct FReadScopeLock { explicit FReadScopeLock(FRWLock&L):Lock(L.Mutex){} std::shared_lock Lock; }; struct FWriteScopeLock { explicit FWriteScopeLock(FRWLock&L):Lock(L.Mutex){} std::unique_lock Lock; }; enum EConsoleVariableFlags { ECVF_Default=0, ECVF_SetByCommandline=1, ECVF_SetByCode=2 }; class FAutoConsoleVariableRef { public:FAutoConsoleVariableRef(const char*,int32&,const char*,uint32=0){} FAutoConsoleVariableRef(const char*,float&,const char*,uint32=0){} FAutoConsoleVariableRef(const char*,bool&,const char*,uint32=0){} }; template class TAutoConsoleVariable { public:TAutoConsoleVariable(const char*,T Default,const char*,uint32=0):Value(Default){}T GetValueOnAnyThread()const{return Value.load();}T GetValueOnGameThread()const{return Value.load();}void Set(T V,uint32=0){Value.store(V);} TAutoConsoleVariable*operator->(){return this;}private:std::atomicValue;}; struct FCommandLine { inline static std::string Arguments; static const char* Get(){return Arguments.c_str();} static void Set(std::string_view InArguments){Arguments.assign(InArguments);} }; struct FParse { static bool ReadValue(const char* CommandLine,const char* Key,std::string& Out) { if(!CommandLine||!Key||!*Key)return false; const std::string_view Command(CommandLine),Token(Key); std::size_t Position=0; while((Position=Command.find(Token,Position))!=std::string_view::npos) { const bool bTokenStart=Position==0||Command[Position-1]==' ' ||Command[Position-1]=='\t'||Command[Position-1]=='-'; const std::size_t ValueStart=Position+Token.size(); if(bTokenStart&&ValueStart<=Command.size()) { std::size_t ValueEnd=Command.find_first_of(" \t\r\n",ValueStart); if(ValueEnd==std::string_view::npos)ValueEnd=Command.size(); if(ValueEnd>ValueStart) {Out.assign(Command.substr(ValueStart,ValueEnd-ValueStart));return true;} } Position+=Token.size(); } return false; } static bool Value(const char* CommandLine,const char* Key,int32& Out) { std::string Text; if(!ReadValue(CommandLine,Key,Text))return false; const auto Result=std::from_chars(Text.data(),Text.data()+Text.size(),Out); return Result.ec==std::errc{}&&Result.ptr==Text.data()+Text.size(); } static bool Value(const char* CommandLine,const char* Key,float& Out) { std::string Text; if(!ReadValue(CommandLine,Key,Text))return false; char* End=nullptr;Out=std::strtof(Text.c_str(),&End); return End==Text.c_str()+Text.size(); } static bool Value(const char* CommandLine,const char* Key,FString& Out) { std::string Text; if(!ReadValue(CommandLine,Key,Text))return false; Out=FString(Text);return true; } static bool Param(const char* CommandLine,const char* Name) { if(!CommandLine||!Name||!*Name)return false; const std::string_view Command(CommandLine),Token(Name); std::size_t Position=0; while((Position=Command.find(Token,Position))!=std::string_view::npos) { const bool bTokenStart=Position==0||Command[Position-1]==' ' ||Command[Position-1]=='\t'||Command[Position-1]=='-'; const std::size_t End=Position+Token.size(); const bool bTokenEnd=End==Command.size()||Command[End]==' ' ||Command[End]=='\t'||Command[End]=='\r'||Command[End]=='\n'; if(bTokenStart&&bTokenEnd)return true; Position=End; } return false; } }; struct FCrc { static uint32 MemCrc32(const void*Data,int32 Length,uint32 Crc=0){const auto*P=static_cast(Data);uint32 V=~Crc;for(int32 I=0;I>1)^(0xEDB88320u&static_cast(-static_cast(V&1)));}return ~V;} static uint32 StrCrc32(const char*S,uint32 Crc=0){return MemCrc32(S,static_cast(std::strlen(S)),Crc);} }; template using TAtomic = std::atomic; class FThreadSafeCounter { public:int32 Increment(){return ++Value;}int32 Decrement(){return --Value;}int32 GetValue()const{return Value.load();}void Reset(){Value.store(0);}private:std::atomicValue{0};};