From b3d9ab9e8b1e85f34a5357633c61c88b3188bcf0 Mon Sep 17 00:00:00 2001 From: Gray <84999745+Milxnor@users.noreply.github.com> Date: Wed, 20 Mar 2024 21:45:38 -0400 Subject: [PATCH] =?UTF-8?q?Dude=20=F0=9F=92=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Project Reboot 3.0/Actor.cpp | 43 +- Project Reboot 3.0/Actor.h | 13 +- Project Reboot 3.0/ActorChannel.h | 20 + Project Reboot 3.0/Array.h | 49 +- Project Reboot 3.0/BitArray.h | 506 +++---- Project Reboot 3.0/Channel.h | 18 +- Project Reboot 3.0/ChildConnection.h | 9 + Project Reboot 3.0/ChooseClass.h | 18 + Project Reboot 3.0/Class.h | 4 +- .../ContainerAllocationPolicies.h | 575 +++++-- Project Reboot 3.0/DataTable.h | 4 +- Project Reboot 3.0/FortAthenaMutator_GG.h | 4 +- Project Reboot 3.0/FortGameModeAthena.cpp | 5 +- Project Reboot 3.0/FortItemDefinition.cpp | 4 +- Project Reboot 3.0/FortLootLevel.cpp | 4 +- Project Reboot 3.0/FortLootPackage.cpp | 20 +- Project Reboot 3.0/FortPlayerController.cpp | 6 + .../FortWeaponItemDefinition.cpp | 6 + Project Reboot 3.0/Level.cpp | 14 +- Project Reboot 3.0/Level.h | 3 + Project Reboot 3.0/Map.h | 501 ++++-- Project Reboot 3.0/NameTypes.h | 9 +- Project Reboot 3.0/NetConnection.h | 42 +- Project Reboot 3.0/NetDriver.cpp | 1338 ++++++++--------- Project Reboot 3.0/NetDriver.h | 30 +- Project Reboot 3.0/NetworkObjectList.cpp | 5 +- .../NetworkingDistanceConstants.h | 11 + Project Reboot 3.0/Object.cpp | 15 + Project Reboot 3.0/Object.h | 8 + Project Reboot 3.0/PlayerController.h | 2 + Project Reboot 3.0/Project Reboot 3.0.vcxproj | 4 + .../Project Reboot 3.0.vcxproj.filters | 12 + Project Reboot 3.0/Set.h | 510 +++++-- Project Reboot 3.0/SparseArray.h | 364 ++--- Project Reboot 3.0/TimerManager.h | 4 +- Project Reboot 3.0/UnrealMathUtility.h | 87 ++ Project Reboot 3.0/UnrealTypeTraits.h | 8 + Project Reboot 3.0/Vector.h | 12 + Project Reboot 3.0/WeakObjectPtr.h | 13 + Project Reboot 3.0/WeakObjectPtrTemplates.h | 43 +- Project Reboot 3.0/World.h | 12 +- Project Reboot 3.0/WorldSettings.h | 35 + Project Reboot 3.0/addresses.cpp | 6 + Project Reboot 3.0/ai.h | 18 + Project Reboot 3.0/inc.h | 21 + Project Reboot 3.0/reboot.h | 19 +- 46 files changed, 2846 insertions(+), 1608 deletions(-) create mode 100644 Project Reboot 3.0/ChildConnection.h create mode 100644 Project Reboot 3.0/ChooseClass.h create mode 100644 Project Reboot 3.0/NetworkingDistanceConstants.h create mode 100644 Project Reboot 3.0/WorldSettings.h diff --git a/Project Reboot 3.0/Actor.cpp b/Project Reboot 3.0/Actor.cpp index 07ac52a..2ccee7b 100644 --- a/Project Reboot 3.0/Actor.cpp +++ b/Project Reboot 3.0/Actor.cpp @@ -4,6 +4,7 @@ #include "reboot.h" #include "GameplayStatics.h" +#include "Level.h" bool AActor::HasAuthority() { @@ -11,6 +12,11 @@ bool AActor::HasAuthority() return Get(RoleOffset) == 3; } +bool AActor::GetNetDormancy(const FVector& ViewPos, const FVector& ViewDir, AActor* Viewer, AActor* ViewTarget, class UActorChannel* InChannel, float Time, bool bLowBandwidth) // T(REP) +{ + return false; +} + bool AActor::IsTearOff() { static auto bTearOffOffset = GetOffset("bTearOff"); @@ -18,7 +24,12 @@ bool AActor::IsTearOff() return ReadBitfieldValue(bTearOffOffset, bTearOffFieldMask); } -/* FORCEINLINE */ ENetDormancy& AActor::GetNetDormancy() +ULevel* AActor::GetLevel() const +{ + return GetTypedOuter(); +} + +/* FORCEINLINE */ ENetDormancy& AActor::NetDormancy() { static auto NetDormancyOffset = GetOffset("NetDormancy"); return Get(NetDormancyOffset); @@ -38,15 +49,6 @@ FTransform AActor::GetTransform() return Ret; } -/* - -UWorld* AActor::GetWorld() -{ - return GetWorld(); // for real -} - -*/ - void AActor::SetNetDormancy(ENetDormancy Dormancy) { static auto SetNetDormancyFn = FindObject(L"/Script/Engine.Actor.SetNetDormancy"); @@ -63,6 +65,18 @@ AActor* AActor::GetOwner() return Owner; } +FName& AActor::GetNetDriverName() +{ + static auto NetDriverNameOffset = GetOffset("NetDriverName"); + return Get(NetDriverNameOffset); +} + +ENetRole& AActor::GetRemoteRole() +{ + static auto RemoteRoleOffset = GetOffset("RemoteRole"); + return Get(RemoteRoleOffset); +} + void AActor::K2_DestroyActor() { static auto DestroyActorFn = FindObject("/Script/Engine.Actor.K2_DestroyActor"); @@ -195,12 +209,12 @@ void AActor::ForceNetUpdate() this->ProcessEvent(ForceNetUpdateFn); } -bool AActor::IsNetStartupActor() +bool AActor::IsNetStartupActor() // T(REP) { return IsNetStartup(); // The implementation on this function depends on the version. } -bool AActor::IsPendingKillPending() +bool AActor::IsPendingKillPending() // T(REP) { return IsActorBeingDestroyed() || !IsValidChecked(this); } @@ -246,6 +260,11 @@ const AActor* AActor::GetNetOwner() const return GetNetOwnerOriginal(this); } +bool AActor::IsActorInitialized() // T(REP) +{ + return true; +} + void AActor::GetActorEyesViewPoint(FVector* OutLocation, FRotator* OutRotation) const { static auto GetActorEyesViewPointFn = FindObject(L"/Script/Engine.Actor.GetActorEyesViewPoint"); diff --git a/Project Reboot 3.0/Actor.h b/Project Reboot 3.0/Actor.h index fceac71..c70c77f 100644 --- a/Project Reboot 3.0/Actor.h +++ b/Project Reboot 3.0/Actor.h @@ -2,6 +2,7 @@ #include "Object.h" #include "anticheat.h" +#include "Vector.h" enum class ENetDormancy : uint8_t { @@ -17,15 +18,19 @@ enum class ENetDormancy : uint8_t class AActor : public UObject { public: + static inline void (*originalCallPreReplication)(AActor*, class UNetDriver*); struct FTransform GetTransform(); - // class UWorld* GetWorld(); + bool GetNetDormancy(const FVector& ViewPos, const FVector& ViewDir, AActor* Viewer, AActor* ViewTarget, class UActorChannel* InChannel, float Time, bool bLowBandwidth); bool HasAuthority(); bool IsTearOff(); - /* FORCEINLINE */ ENetDormancy& GetNetDormancy(); + class ULevel* GetLevel() const; + /* FORCEINLINE */ ENetDormancy& NetDormancy(); int32& GetNetTag(); void SetNetDormancy(ENetDormancy Dormancy); AActor* GetOwner(); + FName& GetNetDriverName(); + ENetRole& GetRemoteRole(); struct FVector GetActorScale3D(); struct FVector GetActorLocation(); struct FVector GetActorForwardVector(); @@ -52,12 +57,12 @@ public: float& GetNetUpdateFrequency(); float& GetMinNetUpdateFrequency(); const AActor* GetNetOwner() const; + bool IsActorInitialized(); void GetActorEyesViewPoint(FVector* OutLocation, FRotator* OutRotation) const; AActor* GetClosestActor(UClass* ActorClass, float DistMax, std::function AdditionalCheck = [&](AActor*) { return true; }); - bool IsRelevancyOwnerFor(const AActor* ReplicatedActor, const AActor* ActorOwner, const AActor* ConnectionActor) const + bool IsRelevancyOwnerFor(const AActor* ReplicatedActor, const AActor* ActorOwner, const AActor* ConnectionActor) const // T(REP) { - // we should call virtual function but eh // return (ActorOwner == this); static auto IsRelevancyOwnerForOffset = 0x428; diff --git a/Project Reboot 3.0/ActorChannel.h b/Project Reboot 3.0/ActorChannel.h index 6603d78..368941d 100644 --- a/Project Reboot 3.0/ActorChannel.h +++ b/Project Reboot 3.0/ActorChannel.h @@ -3,9 +3,29 @@ #include "Channel.h" #include "NetworkGuid.h" +enum ESetChannelActorFlags +{ + None1 = 0, // Bro compiler what + SkipReplicatorCreation = (1 << 0), + SkipMarkActive = (1 << 1), +}; + class UActorChannel : public UChannel { public: + static inline void (*originalSetChannelActor)(UActorChannel*, AActor*); + static inline __int64 (*originalReplicateActor)(UActorChannel*); + + void SetChannelActor(AActor* Actor, ESetChannelActorFlags Flags) + { + originalSetChannelActor(this, Actor); // T(REP) ADD FLAGS FOR NEWER BUILDS + } + + __int64 ReplicateActor() // Returns how many bits were replicated (does not include non-bunch packet overhead) + { + return originalReplicateActor(this); + } + double& GetLastUpdateTime() { static auto LastUpdateTimeOffset = GetOffset("Actor") + 8 + 4 + 4 + 8; // checked on 4.19 diff --git a/Project Reboot 3.0/Array.h b/Project Reboot 3.0/Array.h index 5bd267f..6e592ff 100644 --- a/Project Reboot 3.0/Array.h +++ b/Project Reboot 3.0/Array.h @@ -5,17 +5,7 @@ #include "MemoryOps.h" #include "ContainerAllocationPolicies.h" - -struct FMemory -{ - static inline void* (*Realloc)(void* Original, SIZE_T Count, uint32_t Alignment /* = DEFAULT_ALIGNMENT */); -}; - -template -static T* AllocUnreal(size_t Size) -{ - return (T*)FMemory::Realloc(0, Size, 0); -} +#include "IsPointer.h" template //, typename InAllocatorType> class TArray @@ -26,6 +16,7 @@ public: using ElementAllocatorType = InElementType*; using SizeType = int32; + typedef InElementType ElementType; ElementAllocatorType Data = nullptr; // AllocatorInstance; SizeType ArrayNum; @@ -33,14 +24,46 @@ public: public: +#if TARRAY_RANGED_FOR_CHECKS + typedef TCheckedPointerIterator< ElementType, SizeType> RangedForIteratorType; + typedef TCheckedPointerIterator RangedForConstIteratorType; +#else + typedef ElementType* RangedForIteratorType; + typedef const ElementType* RangedForConstIteratorType; +#endif + +#if TARRAY_RANGED_FOR_CHECKS + FORCEINLINE friend RangedForIteratorType begin(TArray& Array) { return RangedForIteratorType(Array.ArrayNum, Array.GetData()); } + FORCEINLINE friend RangedForConstIteratorType begin(const TArray& Array) { return RangedForConstIteratorType(Array.ArrayNum, Array.GetData()); } + FORCEINLINE friend RangedForIteratorType end(TArray& Array) { return RangedForIteratorType(Array.ArrayNum, Array.GetData() + Array.Num()); } + FORCEINLINE friend RangedForConstIteratorType end(const TArray& Array) { return RangedForConstIteratorType(Array.ArrayNum, Array.GetData() + Array.Num()); } +#else + FORCEINLINE friend RangedForIteratorType begin(TArray& Array) { return Array.GetData(); } + FORCEINLINE friend RangedForConstIteratorType begin(const TArray& Array) { return Array.GetData(); } + FORCEINLINE friend RangedForIteratorType end(TArray& Array) { return Array.GetData() + Array.Num(); } + FORCEINLINE friend RangedForConstIteratorType end(const TArray& Array) { return Array.GetData() + Array.Num(); } +#endif + inline InElementType& At(int i, size_t Size = sizeof(InElementType)) const { return *(InElementType*)(__int64(Data) + (static_cast(Size) * i)); } inline InElementType& at(int i, size_t Size = sizeof(InElementType)) const { return *(InElementType*)(__int64(Data) + (static_cast(Size) * i)); } inline InElementType* AtPtr(int i, size_t Size = sizeof(InElementType)) const { return (InElementType*)(__int64(Data) + (static_cast(Size) * i)); } bool IsValidIndex(int i) { return i > 0 && i < ArrayNum; } - ElementAllocatorType& GetData() const { return Data; } - ElementAllocatorType& GetData() { return Data; } + InElementType* GetData() + { + return (InElementType*)Data; + } + + /** + * Helper function for returning a typed pointer to the first array entry. + * + * @returns Pointer to first array entry or nullptr if ArrayMax == 0. + */ + const InElementType* GetData() const + { + return (const InElementType*)Data; + } void Reserve(int Number, size_t Size = sizeof(InElementType)) { diff --git a/Project Reboot 3.0/BitArray.h b/Project Reboot 3.0/BitArray.h index fc07d9c..dc51d3d 100644 --- a/Project Reboot 3.0/BitArray.h +++ b/Project Reboot 3.0/BitArray.h @@ -1,329 +1,225 @@ #pragma once #include "ContainerAllocationPolicies.h" - -static FORCEINLINE uint32 CountLeadingZeros(uint32 Value) -{ - unsigned long Log2; - if (_BitScanReverse(&Log2, Value) != 0) - { - return 31 - Log2; - } - - return 32; -} +#include "UnrealMathUtility.h" #define NumBitsPerDWORD ((int32)32) #define NumBitsPerDWORDLogTwo ((int32)5) +template +class TBitArray; + +template +class TConstSetBitIterator; + +template class TBitArray { public: - TInlineAllocator<4>::ForElementType Data; - int NumBits; - int MaxBits; + /** + * Move constructor. + */ + FORCEINLINE TBitArray(TBitArray&& Other) + { + MoveOrCopy(*this, Other); + } - struct FRelativeBitReference - { - public: - FORCEINLINE explicit FRelativeBitReference(int32 BitIndex) - : DWORDIndex(BitIndex >> NumBitsPerDWORDLogTwo) - , Mask(1 << (BitIndex & (NumBitsPerDWORD -1))) - { - } + FORCEINLINE int32 Num() const { return NumBits; } - int32 DWORDIndex; - uint32 Mask; - }; + /** + * Copy constructor. + */ + FORCEINLINE TBitArray(const TBitArray& Copy) + : NumBits(0) + , MaxBits(0) + { + *this = Copy; + } + FORCEINLINE const uint32* GetData() const + { + return (uint32*)AllocatorInstance.GetAllocation(); + } + + FORCEINLINE uint32* GetData() + { + return (uint32*)AllocatorInstance.GetAllocation(); + } + + /** + * Move assignment. + */ + FORCEINLINE TBitArray& operator=(TBitArray&& Other) + { + if (this != &Other) + { + MoveOrCopy(*this, Other); + } + + return *this; + } + +private: + typedef typename Allocator::template ForElementType AllocatorType; + + AllocatorType AllocatorInstance; + int32 NumBits; + int32 MaxBits; + + template + static FORCEINLINE typename TEnableIf::MoveWillEmptyContainer>::Type MoveOrCopy(BitArrayType& ToArray, BitArrayType& FromArray) + { + ToArray.AllocatorInstance.MoveToEmpty(FromArray.AllocatorInstance); + + ToArray.NumBits = FromArray.NumBits; + ToArray.MaxBits = FromArray.MaxBits; + FromArray.NumBits = 0; + FromArray.MaxBits = 0; + } + + template + static FORCEINLINE typename TEnableIf::MoveWillEmptyContainer>::Type MoveOrCopy(BitArrayType& ToArray, BitArrayType& FromArray) + { + ToArray = FromArray; + } + + FORCENOINLINE void Realloc(int32 PreviousNumBits) + { + const int32 PreviousNumDWORDs = FMath::DivideAndRoundUp(PreviousNumBits, NumBitsPerDWORD); + const int32 MaxDWORDs = FMath::DivideAndRoundUp(MaxBits, NumBitsPerDWORD); + + AllocatorInstance.ResizeAllocation(PreviousNumDWORDs, MaxDWORDs, sizeof(uint32)); + + if (MaxDWORDs) + { + // Reset the newly allocated slack DWORDs. + FMemory::Memzero((uint32*)AllocatorInstance.GetAllocation() + PreviousNumDWORDs, (MaxDWORDs - PreviousNumDWORDs) * sizeof(uint32)); + } + } public: - struct FBitReference - { - FORCEINLINE FBitReference(uint32& InData, uint32 InMask) - : Data(InData) - , Mask(InMask) - { - } - FORCEINLINE const FBitReference(const uint32& InData, const uint32 InMask) - : Data(const_cast(InData)) - , Mask(InMask) - { - } - - FORCEINLINE void SetBit(const bool Value) - { - Value ? Data |= Mask : Data &= ~Mask; - - // 10011101 - Data // 10011101 - Data - // 00000010 - Mask - true | // 00000010 - Mask - false - // 10011111 - |= // 11111101 - ~ - // // 10011111 - &= - } - - FORCEINLINE operator bool() const - { - return (Data & Mask) != 0; - } - FORCEINLINE void operator=(const bool Value) - { - this->SetBit(Value); - } - - private: - uint32& Data; - uint32 Mask; - }; +}; +class FRelativeBitReference +{ public: - class FBitIterator : public FRelativeBitReference - { - private: - int32 Index; - const TBitArray& IteratedArray; + FORCEINLINE explicit FRelativeBitReference(int32 BitIndex) + : DWORDIndex(BitIndex >> NumBitsPerDWORDLogTwo) + , Mask(1 << (BitIndex & (NumBitsPerDWORD - 1))) + { + } - public: - FORCEINLINE const FBitIterator(const TBitArray& ToIterate, const int32 StartIndex) // Begin - : IteratedArray(ToIterate) - , Index(StartIndex) - , FRelativeBitReference(StartIndex) - { - } - FORCEINLINE const FBitIterator(const TBitArray& ToIterate) // End - : IteratedArray(ToIterate) - , Index(ToIterate.NumBits) - , FRelativeBitReference(ToIterate.NumBits) - { - } - - FORCEINLINE explicit operator bool() const - { - return Index < IteratedArray.Num(); - } - FORCEINLINE FBitIterator& operator++() - { - ++Index; - this->Mask <<= 1; - if (!this->Mask) - { - this->Mask = 1; - ++this->DWORDIndex; - } - return *this; - } - FORCEINLINE bool operator*() const - { - // Thesis: Once there are more elements in the BitArray than InlineData can hold it'll just allocate all of - // them through SecondaryElements, leaving InlineData all true - - if (IteratedArray.NumBits < IteratedArray.Data.NumInlineBits()) - { - return (bool)FBitReference(IteratedArray.Data.GetInlineElement(this->DWORDIndex), this->Mask); - } - else - { - return (bool)FBitReference(IteratedArray.Data.GetSecondaryElement(this->DWORDIndex), this->Mask); - } - } - FORCEINLINE bool operator==(const FBitIterator& OtherIt) const - { - return Index == OtherIt.Index; - } - FORCEINLINE bool operator!=(const FBitIterator& OtherIt) const - { - return Index (const int32 Other) const - { - return Index < Other; - } - - FORCEINLINE int32 GetIndex() const - { - return Index; - } - }; - - class FSetBitIterator : public FRelativeBitReference - { - private: - const TBitArray& IteratedArray; - - uint32 UnvisitedBitMask; - int32 CurrentBitIndex; - int32 BaseBitIndex; - - public: - FORCEINLINE FSetBitIterator(const TBitArray& ToIterate, int32 StartIndex) - : FRelativeBitReference(StartIndex) - , IteratedArray(const_cast(ToIterate)) - , UnvisitedBitMask((~0U) << (StartIndex & (NumBitsPerDWORD - 1))) - , CurrentBitIndex(StartIndex) - , BaseBitIndex(StartIndex & ~(NumBitsPerDWORD - 1)) - { - if (StartIndex != IteratedArray.NumBits) - { - FindNextSetBit(); - } - } - FORCEINLINE FSetBitIterator(const TBitArray& ToIterate) - : FRelativeBitReference(ToIterate.NumBits) - , IteratedArray(const_cast(ToIterate)) - , UnvisitedBitMask(0) - , CurrentBitIndex(ToIterate.NumBits) - , BaseBitIndex(ToIterate.NumBits) - { - } - - FORCEINLINE FSetBitIterator& operator++() - { - UnvisitedBitMask &= ~this->Mask; - - FindNextSetBit(); - - return *this; - } - FORCEINLINE bool operator*() const - { - return true; - } - - FORCEINLINE bool operator==(const FSetBitIterator& Other) const - { - return CurrentBitIndex == Other.CurrentBitIndex; - } - FORCEINLINE bool operator!=(const FSetBitIterator& Other) const - { - return CurrentBitIndex DWORDIndex] & UnvisitedBitMask; - - while (!RemainingBitMask) - { - ++this->DWORDIndex; - BaseBitIndex += NumBitsPerDWORD; - - if (this->DWORDIndex > LastDWORDIndex) - { - CurrentBitIndex = ArrayNum; - return; - } - - RemainingBitMask = ArrayData[this->DWORDIndex]; - UnvisitedBitMask = ~0; - } - - const uint32 NewRemainingBitMask = RemainingBitMask & (RemainingBitMask - 1); - - this->Mask = NewRemainingBitMask ^ RemainingBitMask; - - CurrentBitIndex = BaseBitIndex + NumBitsPerDWORD - 1 - CountLeadingZeros(this->Mask); - - if (CurrentBitIndex > ArrayNum) - { - CurrentBitIndex = ArrayNum; - } - } - }; + int32 DWORDIndex; + uint32 Mask; +}; +template +class TConstSetBitIterator : public FRelativeBitReference +{ public: - FORCEINLINE FBitIterator Iterator(int32 StartIndex) - { - return FBitIterator(*this, StartIndex); - } - FORCEINLINE FSetBitIterator SetBitIterator(int32 StartIndex) - { - return FSetBitIterator(*this, StartIndex); - } - FORCEINLINE FBitIterator begin() - { - return FBitIterator(*this, 0); - } - FORCEINLINE const FBitIterator begin() const - { - return FBitIterator(*this, 0); - } - FORCEINLINE FBitIterator end() - { - return FBitIterator(*this); - } - FORCEINLINE const FBitIterator end() const - { - return FBitIterator(*this); - } + /** Constructor. */ + TConstSetBitIterator(const TBitArray& InArray, int32 StartIndex = 0) + : FRelativeBitReference(StartIndex) + , Array(InArray) + , UnvisitedBitMask((~0U) << (StartIndex & (NumBitsPerDWORD - 1))) + , CurrentBitIndex(StartIndex) + , BaseBitIndex(StartIndex & ~(NumBitsPerDWORD - 1)) + { + // check(StartIndex >= 0 && StartIndex <= Array.Num()); + if (StartIndex != Array.Num()) + { + FindFirstSetBit(); + } + } - FORCEINLINE FSetBitIterator SetBitsItBegin() - { - return FSetBitIterator(*this, 0); - } - FORCEINLINE const FSetBitIterator SetBitsItBegin() const - { - return FSetBitIterator(*this, 0); - } - FORCEINLINE const FSetBitIterator SetBitsItEnd() - { - return FSetBitIterator(*this); - } - FORCEINLINE const FSetBitIterator SetBitsItEnd() const - { - return FSetBitIterator(*this); - } + /** Forwards iteration operator. */ + FORCEINLINE TConstSetBitIterator& operator++() + { + // Mark the current bit as visited. + UnvisitedBitMask &= ~this->Mask; - FORCEINLINE int32 Num() const - { - return NumBits; - } - FORCEINLINE int32 Max() const - { - return MaxBits; - } - FORCEINLINE bool IsSet(int32 Index) const - { - return *FBitIterator(*this, Index); - } - FORCEINLINE void Set(const int32 Index, const bool Value, bool bIsSettingAllZero = false) - { - const int32 DWORDIndex = (Index >> ((int32)5)); - const int32 Mask = (1 << (Index & (((int32)32) - 1))); + // Find the first set bit that hasn't been visited yet. + FindFirstSetBit(); - if (!bIsSettingAllZero) - NumBits = Index >= NumBits ? Index < MaxBits ? Index + 1 : NumBits : NumBits; + return *this; + } - FBitReference(Data[DWORDIndex], Mask).SetBit(Value); - } - FORCEINLINE void ZeroAll() - { - for (int i = 0; i < MaxBits; ++i) - { - Set(i, false, true); - } - } -}; \ No newline at end of file + FORCEINLINE friend bool operator==(const TConstSetBitIterator& Lhs, const TConstSetBitIterator& Rhs) + { + // We only need to compare the bit index and the array... all the rest of the state is unobservable. + return Lhs.CurrentBitIndex == Rhs.CurrentBitIndex && &Lhs.Array == &Rhs.Array; + } + + FORCEINLINE friend bool operator!=(const TConstSetBitIterator& Lhs, const TConstSetBitIterator& Rhs) + { + return !(Lhs == Rhs); + } + + /** conversion to "bool" returning true if the iterator is valid. */ + FORCEINLINE explicit operator bool() const + { + return CurrentBitIndex < Array.Num(); + } + /** inverse of the "bool" operator */ + FORCEINLINE bool operator !() const + { + return !(bool)*this; + } + + /** Index accessor. */ + FORCEINLINE int32 GetIndex() const + { + return CurrentBitIndex; + } + +private: + + const TBitArray& Array; + + uint32 UnvisitedBitMask; + int32 CurrentBitIndex; + int32 BaseBitIndex; + + + /** Find the first set bit starting with the current bit, inclusive. */ + void FindFirstSetBit() + { + const uint32* ArrayData = Array.GetData(); + const int32 ArrayNum = Array.Num(); + const int32 LastDWORDIndex = (ArrayNum - 1) / NumBitsPerDWORD; + + // Advance to the next non-zero uint32. + uint32 RemainingBitMask = ArrayData[this->DWORDIndex] & UnvisitedBitMask; + while (!RemainingBitMask) + { + ++this->DWORDIndex; + BaseBitIndex += NumBitsPerDWORD; + if (this->DWORDIndex > LastDWORDIndex) + { + // We've advanced past the end of the array. + CurrentBitIndex = ArrayNum; + return; + } + + RemainingBitMask = ArrayData[this->DWORDIndex]; + UnvisitedBitMask = ~0; + } + + // This operation has the effect of unsetting the lowest set bit of BitMask + const uint32 NewRemainingBitMask = RemainingBitMask & (RemainingBitMask - 1); + + // This operation XORs the above mask with the original mask, which has the effect + // of returning only the bits which differ; specifically, the lowest bit + this->Mask = NewRemainingBitMask ^ RemainingBitMask; + + // If the Nth bit was the lowest set bit of BitMask, then this gives us N + CurrentBitIndex = BaseBitIndex + NumBitsPerDWORD - 1 - FMath::CountLeadingZeros(this->Mask); + + // If we've accidentally iterated off the end of an array but still within the same DWORD + // then set the index to the last index of the array + if (CurrentBitIndex > ArrayNum) + { + CurrentBitIndex = ArrayNum; + } + } +}; diff --git a/Project Reboot 3.0/Channel.h b/Project Reboot 3.0/Channel.h index 55acb0a..75515f1 100644 --- a/Project Reboot 3.0/Channel.h +++ b/Project Reboot 3.0/Channel.h @@ -2,10 +2,26 @@ #include "Object.h" +enum EChannelType +{ + CHTYPE_None = 0, + CHTYPE_Control = 1, + CHTYPE_Actor = 2, + CHTYPE_File = 3, + CHTYPE_Voice = 4, + CHTYPE_MAX = 8, +}; + +enum EChannelCreateFlags +{ + None = (1 << 0), + OpenedLocally = (1 << 1), +}; + class UChannel : public UObject { public: - void StartBecomingDormant() + void StartBecomingDormant() // T(REP) { void (*StartBecomingDormantOriginal)(UChannel* Channel) = decltype(StartBecomingDormantOriginal)(this->VFTable[0x298 / 8]); StartBecomingDormantOriginal(this); diff --git a/Project Reboot 3.0/ChildConnection.h b/Project Reboot 3.0/ChildConnection.h new file mode 100644 index 0000000..52320a5 --- /dev/null +++ b/Project Reboot 3.0/ChildConnection.h @@ -0,0 +1,9 @@ +#pragma once + +#include "NetConnection.h" + +class UChildConnection : public UNetConnection +{ +public: + +}; \ No newline at end of file diff --git a/Project Reboot 3.0/ChooseClass.h b/Project Reboot 3.0/ChooseClass.h new file mode 100644 index 0000000..cd0dfdc --- /dev/null +++ b/Project Reboot 3.0/ChooseClass.h @@ -0,0 +1,18 @@ +#pragma once + +template +class TChooseClass; + +template +class TChooseClass +{ +public: + typedef TrueClass Result; +}; + +template +class TChooseClass +{ +public: + typedef FalseClass Result; +}; \ No newline at end of file diff --git a/Project Reboot 3.0/Class.h b/Project Reboot 3.0/Class.h index 2b7b4cb..d143283 100644 --- a/Project Reboot 3.0/Class.h +++ b/Project Reboot 3.0/Class.h @@ -68,8 +68,8 @@ public: for (int i = 0; i < Names->Num(); ++i) { auto& Pair = Names->At(i); - auto& Name = Pair.Key(); - auto Value = Pair.Value(); + auto& Name = Pair.Key; + auto Value = Pair.Value; if (Name.ComparisonIndex.Value) { diff --git a/Project Reboot 3.0/ContainerAllocationPolicies.h b/Project Reboot 3.0/ContainerAllocationPolicies.h index 589d42f..0096441 100644 --- a/Project Reboot 3.0/ContainerAllocationPolicies.h +++ b/Project Reboot 3.0/ContainerAllocationPolicies.h @@ -1,95 +1,48 @@ #pragma once #include "NumericLimits.h" +#include "UnrealTemplate.h" +#include "TypeCompatibleBytes.h" -template -class TInlineAllocator +struct FMemory // so real place { -private: - template - struct alignas(Alignment) TAlignedBytes + static inline void* (*Realloc)(void* Original, SIZE_T Count, uint32_t Alignment /* = DEFAULT_ALIGNMENT */); + + static void Free(void* Data) { - unsigned char Pad[Size]; - }; + // We could use actual free.. - template - struct TTypeCompatibleBytes : public TAlignedBytes - { - }; + Realloc(Data, 0, 0); + } -public: - template - class ForElementType - { - friend class TBitArray; - - private: - TTypeCompatibleBytes InlineData[NumElements]; - - ElementType* SecondaryData; - - public: - - FORCEINLINE int32 NumInlineBytes() const - { - return sizeof(ElementType) * NumElements; - } - FORCEINLINE int32 NumInlineBits() const - { - return NumInlineBytes() * 8; - } - - FORCEINLINE ElementType& operator[](int32 Index) - { - return *(ElementType*)(&InlineData[Index]); - } - FORCEINLINE const ElementType& operator[](int32 Index) const - { - return *(ElementType*)(&InlineData[Index]); - } - - FORCEINLINE void operator=(void* InElements) - { - SecondaryData = InElements; - } - - FORCEINLINE ElementType& GetInlineElement(int32 Index) - { - return *(ElementType*)(&InlineData[Index]); - } - FORCEINLINE const ElementType& GetInlineElement(int32 Index) const - { - return *(ElementType*)(&InlineData[Index]); - } - FORCEINLINE ElementType& GetSecondaryElement(int32 Index) - { - return SecondaryData[Index]; - } - FORCEINLINE const ElementType& GetSecondaryElement(int32 Index) const - { - return SecondaryData[Index]; - } - ElementType* GetInlineElements() const - { - return (ElementType*)InlineData; - } - FORCEINLINE ElementType* GetAllocation() const - { - return IfAThenAElseB(SecondaryData, GetInlineElements()); - } - }; -}; - - -FORCEINLINE /*FMEMORY_INLINE_FUNCTION_DECORATOR*/ size_t /*FMemory::*/QuantizeSize(SIZE_T Count, uint32 Alignment) -{ - return Count; - /* - if (!FMEMORY_INLINE_GMalloc) + static size_t QuantizeSize(size_t Count, uint32 Alignment) // T(R) { return Count; } - return FMEMORY_INLINE_GMalloc->QuantizeSize(Count, Alignment); */ + + static void* Memmove(void* Dest, const void* Src, SIZE_T Count) + { + memmove(Dest, Src, Count); + } + + static FORCEINLINE void* Memzero(void* Dest, SIZE_T Count) + { + // return FPlatformMemory::Memzero(Dest, Count); + return RtlSecureZeroMemory(Dest, Count); + } + + template< class T > + static FORCEINLINE void Memzero(T& Src) + { + static_assert(!TIsPointer::Value, "For pointers use the two parameters function"); + Memzero(&Src, sizeof(T)); + } +}; + +template +static T* AllocUnreal(size_t Size) +{ + return (T*)FMemory::Realloc(0, Size, 0); } enum @@ -97,21 +50,467 @@ enum DEFAULT_ALIGNMENT = 0 }; -template -FORCEINLINE SizeType DefaultCalculateSlackReserve(SizeType NumElements, SIZE_T BytesPerElement, bool bAllowQuantize, uint32 Alignment = DEFAULT_ALIGNMENT) +// 4.19 + +template +FORCEINLINE void RelocateConstructItems(void* Dest, const SourceElementType* Source, SizeType Count) { - SizeType Retval = NumElements; + FMemory::Memmove(Dest, Source, sizeof(SourceElementType) * Count); + /* + + if constexpr (UE::Core::Private::MemoryOps::TCanBitwiseRelocate::Value) + { + FMemory::Memmove(Dest, Source, sizeof(SourceElementType) * Count); + } + else + { + while (Count) + { + // We need a typedef here because VC won't compile the destructor call below if SourceElementType itself has a member called SourceElementType + typedef SourceElementType RelocateConstructItemsElementTypeTypedef; + + new (Dest) DestinationElementType(*Source); + ++(DestinationElementType*&)Dest; + (Source++)->RelocateConstructItemsElementTypeTypedef::~RelocateConstructItemsElementTypeTypedef(); + --Count; + } + } + */ +} + +class FDefaultAllocator; + +template +class TInlineAllocator +{ +public: + + enum { NeedsElementType = true }; + enum { RequireRangeCheck = true }; + + template + class ForElementType + { + public: + ForElementType() + { + } + + FORCEINLINE void MoveToEmpty(ForElementType& Other) + { + // checkSlow(this != &Other); + + if (!Other.SecondaryData.GetAllocation()) + { + RelocateConstructItems((void*)InlineData, Other.GetInlineElements(), NumInlineElements); + } + + // Move secondary storage in any case. + // This will move secondary storage if it exists but will also handle the case where secondary storage is used in Other but not in *this. + SecondaryData.MoveToEmpty(Other.SecondaryData); + } + + // FContainerAllocatorInterface + FORCEINLINE ElementType* GetAllocation() const + { + return IfAThenAElseB(SecondaryData.GetAllocation(), GetInlineElements()); + } + + void ResizeAllocation(int32 PreviousNumElements, int32 NumElements, SIZE_T NumBytesPerElement) + { + // Check if the new allocation will fit in the inline data area. + if (NumElements <= NumInlineElements) + { + // If the old allocation wasn't in the inline data area, relocate it into the inline data area. + if (SecondaryData.GetAllocation()) + { + RelocateConstructItems((void*)InlineData, (ElementType*)SecondaryData.GetAllocation(), PreviousNumElements); + + // Free the old indirect allocation. + SecondaryData.ResizeAllocation(0, 0, NumBytesPerElement); + } + } + else + { + if (!SecondaryData.GetAllocation()) + { + // Allocate new indirect memory for the data. + SecondaryData.ResizeAllocation(0, NumElements, NumBytesPerElement); + + // Move the data out of the inline data area into the new allocation. + RelocateConstructItems((void*)SecondaryData.GetAllocation(), GetInlineElements(), PreviousNumElements); + } + else + { + // Reallocate the indirect data for the new size. + SecondaryData.ResizeAllocation(PreviousNumElements, NumElements, NumBytesPerElement); + } + } + } + + FORCEINLINE int32 CalculateSlackReserve(int32 NumElements, SIZE_T NumBytesPerElement) const + { + // If the elements use less space than the inline allocation, only use the inline allocation as slack. + return NumElements <= NumInlineElements ? + NumInlineElements : + SecondaryData.CalculateSlackReserve(NumElements, NumBytesPerElement); + } + FORCEINLINE int32 CalculateSlackShrink(int32 NumElements, int32 NumAllocatedElements, int32 NumBytesPerElement) const + { + // If the elements use less space than the inline allocation, only use the inline allocation as slack. + return NumElements <= NumInlineElements ? + NumInlineElements : + SecondaryData.CalculateSlackShrink(NumElements, NumAllocatedElements, NumBytesPerElement); + } + FORCEINLINE int32 CalculateSlackGrow(int32 NumElements, int32 NumAllocatedElements, int32 NumBytesPerElement) const + { + // If the elements use less space than the inline allocation, only use the inline allocation as slack. + return NumElements <= NumInlineElements ? + NumInlineElements : + SecondaryData.CalculateSlackGrow(NumElements, NumAllocatedElements, NumBytesPerElement); + } + + SIZE_T GetAllocatedSize(int32 NumAllocatedElements, SIZE_T NumBytesPerElement) const + { + return SecondaryData.GetAllocatedSize(NumAllocatedElements, NumBytesPerElement); + } + + bool HasAllocation() + { + return SecondaryData.HasAllocation(); + } + + private: + ForElementType(const ForElementType&); + ForElementType& operator=(const ForElementType&); + + /** The data is stored in this array if less than NumInlineElements is needed. */ + TTypeCompatibleBytes InlineData[NumInlineElements]; + + /** The data is allocated through the indirect allocation policy if more than NumInlineElements is needed. */ + typename SecondaryAllocator::template ForElementType SecondaryData; + + /** @return the base of the aligned inline element data */ + ElementType* GetInlineElements() const + { + return (ElementType*)InlineData; + } + }; + + typedef void ForAnyElementType; +}; + + +struct FScriptContainerElement +{ +}; + +FORCEINLINE int32 DefaultCalculateSlackShrink(int32 NumElements, int32 NumAllocatedElements, SIZE_T BytesPerElement, bool bAllowQuantize, uint32 Alignment = DEFAULT_ALIGNMENT) +{ + int32 Retval; + // checkSlow(NumElements < NumAllocatedElements); + + const uint32 CurrentSlackElements = NumAllocatedElements - NumElements; + const SIZE_T CurrentSlackBytes = (NumAllocatedElements - NumElements) * BytesPerElement; + const bool bTooManySlackBytes = CurrentSlackBytes >= 16384; + const bool bTooManySlackElements = 3 * NumElements < 2 * NumAllocatedElements; + if ((bTooManySlackBytes || bTooManySlackElements) && (CurrentSlackElements > 64 || !NumElements)) // hard coded 64 :-( + { + Retval = NumElements; + if (Retval > 0) + { + if (bAllowQuantize) + { + Retval = FMemory::QuantizeSize(Retval * BytesPerElement, Alignment) / BytesPerElement; + } + } + } + else + { + Retval = NumAllocatedElements; + } + + return Retval; +} + +FORCEINLINE int32 DefaultCalculateSlackGrow(int32 NumElements, int32 NumAllocatedElements, SIZE_T BytesPerElement, bool bAllowQuantize, uint32 Alignment = DEFAULT_ALIGNMENT) +{ + int32 Retval; + // checkSlow(NumElements > NumAllocatedElements && NumElements > 0); + + SIZE_T Grow = 4; + if (NumAllocatedElements || SIZE_T(NumElements) > Grow) + { + // Allocate slack for the array proportional to its size. + Grow = SIZE_T(NumElements) + 3 * SIZE_T(NumElements) / 8 + 16; + } + if (bAllowQuantize) + { + Retval = FMemory::QuantizeSize(Grow * BytesPerElement, Alignment) / BytesPerElement; + } + else + { + Retval = Grow; + } + + if (NumElements > Retval) + { + Retval = MAX_int32; + } + + return Retval; +} + +FORCEINLINE int32 DefaultCalculateSlackReserve(int32 NumElements, SIZE_T BytesPerElement, bool bAllowQuantize, uint32 Alignment = DEFAULT_ALIGNMENT) +{ + int32 Retval = NumElements; // checkSlow(NumElements > 0); if (bAllowQuantize) { - auto Count = SIZE_T(Retval) * SIZE_T(BytesPerElement); - Retval = (SizeType)(QuantizeSize(Count, Alignment) / BytesPerElement); + Retval = FMemory::QuantizeSize(SIZE_T(Retval) * SIZE_T(BytesPerElement), Alignment) / BytesPerElement; // NumElements and MaxElements are stored in 32 bit signed integers so we must be careful not to overflow here. if (NumElements > Retval) { - Retval = TNumericLimits::Max(); + Retval = MAX_int32; } } return Retval; -} \ No newline at end of file +} + +class FHeapAllocator +{ +public: + + enum { NeedsElementType = false }; + enum { RequireRangeCheck = true }; + + class ForAnyElementType + { + public: + /** Default constructor. */ + ForAnyElementType() + : Data(nullptr) + {} + + FORCEINLINE void MoveToEmpty(ForAnyElementType& Other) + { + // checkSlow(this != &Other); + + if (Data) + { + FMemory::Free(Data); + } + + Data = Other.Data; + Other.Data = nullptr; + } + + /** Destructor. */ + FORCEINLINE ~ForAnyElementType() + { + if (Data) + { + FMemory::Free(Data); + } + } + + // FContainerAllocatorInterface + FORCEINLINE FScriptContainerElement* GetAllocation() const + { + return Data; + } + FORCEINLINE void ResizeAllocation(int32 PreviousNumElements, int32 NumElements, SIZE_T NumBytesPerElement) + { + // Avoid calling FMemory::Realloc( nullptr, 0 ) as ANSI C mandates returning a valid pointer which is not what we want. + if (Data || NumElements) + { + //checkSlow(((uint64)NumElements*(uint64)ElementTypeInfo.GetSize() < (uint64)INT_MAX)); + Data = (FScriptContainerElement*)FMemory::Realloc(Data, NumElements * NumBytesPerElement, DEFAULT_ALIGNMENT); + } + } + FORCEINLINE int32 CalculateSlackReserve(int32 NumElements, int32 NumBytesPerElement) const + { + return DefaultCalculateSlackReserve(NumElements, NumBytesPerElement, true); + } + FORCEINLINE int32 CalculateSlackShrink(int32 NumElements, int32 NumAllocatedElements, int32 NumBytesPerElement) const + { + return DefaultCalculateSlackShrink(NumElements, NumAllocatedElements, NumBytesPerElement, true); + } + FORCEINLINE int32 CalculateSlackGrow(int32 NumElements, int32 NumAllocatedElements, int32 NumBytesPerElement) const + { + return DefaultCalculateSlackGrow(NumElements, NumAllocatedElements, NumBytesPerElement, true); + } + + SIZE_T GetAllocatedSize(int32 NumAllocatedElements, SIZE_T NumBytesPerElement) const + { + return NumAllocatedElements * NumBytesPerElement; + } + + bool HasAllocation() + { + return !!Data; + } + + private: + ForAnyElementType(const ForAnyElementType&); + ForAnyElementType& operator=(const ForAnyElementType&); + + /** A pointer to the container's elements. */ + FScriptContainerElement* Data; + }; + + template + class ForElementType : public ForAnyElementType + { + public: + + /** Default constructor. */ + ForElementType() + {} + + FORCEINLINE ElementType* GetAllocation() const + { + return (ElementType*)ForAnyElementType::GetAllocation(); + } + }; +}; + + +class FDefaultAllocator; +class FDefaultBitArrayAllocator; + +/** Encapsulates the allocators used by a sparse array in a single type. */ +template +class TSparseArrayAllocator +{ +public: + + typedef InElementAllocator ElementAllocator; + typedef InBitArrayAllocator BitArrayAllocator; +}; + +/** An inline sparse array allocator that allows sizing of the inline allocations for a set number of elements. */ +template< + uint32 NumInlineElements, + typename SecondaryAllocator = TSparseArrayAllocator +> +class TInlineSparseArrayAllocator +{ +private: + + /** The size to allocate inline for the bit array. */ + enum { InlineBitArrayDWORDs = (NumInlineElements + NumBitsPerDWORD - 1) / NumBitsPerDWORD }; + +public: + + typedef TInlineAllocator ElementAllocator; + typedef TInlineAllocator BitArrayAllocator; +}; + + + +#define DEFAULT_NUMBER_OF_ELEMENTS_PER_HASH_BUCKET 2 +#define DEFAULT_BASE_NUMBER_OF_HASH_BUCKETS 8 +#define DEFAULT_MIN_NUMBER_OF_HASHED_ELEMENTS 4 + +static FORCEINLINE uint32 CountLeadingZeros(uint32 Value) +{ + unsigned long Log2; + if (_BitScanReverse(&Log2, Value) != 0) + { + return 31 - Log2; + } + + return 32; +} + +static /* constexpr */ FORCEINLINE uint32 CeilLogTwo(uint32 Arg) // Milxnor: really in FPlatformMath +{ + Arg = Arg ? Arg : 1; + return 32 - CountLeadingZeros(Arg - 1); +} + +static /* constexpr */ FORCEINLINE uint32 RoundUpToPowerOfTwo(uint32 Arg) // Milxnor: really in FPlatformMath +{ + return 1 << CeilLogTwo(Arg); +} + +template< + typename InSparseArrayAllocator = TSparseArrayAllocator<>, + typename InHashAllocator = TInlineAllocator<1, FDefaultAllocator>, + uint32 AverageNumberOfElementsPerHashBucket = DEFAULT_NUMBER_OF_ELEMENTS_PER_HASH_BUCKET, + uint32 BaseNumberOfHashBuckets = DEFAULT_BASE_NUMBER_OF_HASH_BUCKETS, + uint32 MinNumberOfHashedElements = DEFAULT_MIN_NUMBER_OF_HASHED_ELEMENTS +> +class TSetAllocator +{ +public: + + /** Computes the number of hash buckets to use for a given number of elements. */ + static FORCEINLINE uint32 GetNumberOfHashBuckets(uint32 NumHashedElements) + { + if (NumHashedElements >= MinNumberOfHashedElements) + { + return RoundUpToPowerOfTwo(NumHashedElements / AverageNumberOfElementsPerHashBucket + BaseNumberOfHashBuckets); + } + + return 1; + } + + typedef InSparseArrayAllocator SparseArrayAllocator; + typedef InHashAllocator HashAllocator; +}; + +class FDefaultAllocator; + +/** An inline set allocator that allows sizing of the inline allocations for a set number of elements. */ +template< + uint32 NumInlineElements, + typename SecondaryAllocator = TSetAllocator, FDefaultAllocator>, + uint32 AverageNumberOfElementsPerHashBucket = DEFAULT_NUMBER_OF_ELEMENTS_PER_HASH_BUCKET, + uint32 MinNumberOfHashedElements = DEFAULT_MIN_NUMBER_OF_HASHED_ELEMENTS +> +class TInlineSetAllocator +{ +private: + + enum { NumInlineHashBuckets = (NumInlineElements + AverageNumberOfElementsPerHashBucket - 1) / AverageNumberOfElementsPerHashBucket }; + + static_assert(!(NumInlineHashBuckets& (NumInlineHashBuckets - 1)), "Number of inline buckets must be a power of two"); + +public: + + /** Computes the number of hash buckets to use for a given number of elements. */ + static FORCEINLINE uint32 GetNumberOfHashBuckets(uint32 NumHashedElements) + { + const uint32 NumDesiredHashBuckets = RoundUpToPowerOfTwo(NumHashedElements / AverageNumberOfElementsPerHashBucket); + if (NumDesiredHashBuckets < NumInlineHashBuckets) + { + return NumInlineHashBuckets; + } + + if (NumHashedElements < MinNumberOfHashedElements) + { + return NumInlineHashBuckets; + } + + return NumDesiredHashBuckets; + } + + typedef TInlineSparseArrayAllocator SparseArrayAllocator; + typedef TInlineAllocator HashAllocator; +}; + + +/** + * 'typedefs' for various allocator defaults. + * + * These should be replaced with actual typedefs when Core.h include order is sorted out, as then we won't need to + * 'forward' these TAllocatorTraits specializations below. + */ + +class FDefaultAllocator : public FHeapAllocator { public: typedef FHeapAllocator Typedef; }; +class FDefaultSetAllocator : public TSetAllocator<> { public: typedef TSetAllocator<> Typedef; }; +class FDefaultBitArrayAllocator : public TInlineAllocator<4> { public: typedef TInlineAllocator<4> Typedef; }; +class FDefaultSparseArrayAllocator : public TSparseArrayAllocator<> { public: typedef TSparseArrayAllocator<> Typedef; }; diff --git a/Project Reboot 3.0/DataTable.h b/Project Reboot 3.0/DataTable.h index 06007e5..291f6e8 100644 --- a/Project Reboot 3.0/DataTable.h +++ b/Project Reboot 3.0/DataTable.h @@ -14,11 +14,11 @@ class UDataTable : public UObject { public: template - TMap& GetRowMap() + TMap& GetRowMap() { static auto RowStructOffset = FindOffsetStruct("/Script/Engine.DataTable", "RowStruct"); - return *(TMap*)(__int64(this) + (RowStructOffset + sizeof(UObject*))); // because after rowstruct is rowmap + return *(TMap*)(__int64(this) + (RowStructOffset + sizeof(UObject*))); // because after rowstruct is rowmap } static UClass* StaticClass() diff --git a/Project Reboot 3.0/FortAthenaMutator_GG.h b/Project Reboot 3.0/FortAthenaMutator_GG.h index 88df036..47ddd37 100644 --- a/Project Reboot 3.0/FortAthenaMutator_GG.h +++ b/Project Reboot 3.0/FortAthenaMutator_GG.h @@ -78,9 +78,9 @@ public: for (auto& AwardEntry : AwardEntriesAtElimMap) { - if (AwardEntry.First == Value) + if (AwardEntry.Key == Value) { - return AwardEntry.Second; + return AwardEntry.Value; } } } diff --git a/Project Reboot 3.0/FortGameModeAthena.cpp b/Project Reboot 3.0/FortGameModeAthena.cpp index cb165ae..ca6b758 100644 --- a/Project Reboot 3.0/FortGameModeAthena.cpp +++ b/Project Reboot 3.0/FortGameModeAthena.cpp @@ -109,8 +109,8 @@ FName AFortGameModeAthena::RedirectLootTier(const FName& LootTier) for (auto& Pair : RedirectAthenaLootTierGroups) { - auto& Key = Pair.Key(); - auto& Value = Pair.Value(); + auto& Key = Pair.Key; + auto& Value = Pair.Value; // LOG_INFO(LogDev, "[{}] {} {}", i, Key.ComparisonIndex.Value ? Key.ToString() : "NULL", Key.ComparisonIndex.Value ? Value.ToString() : "NULL"); @@ -428,6 +428,7 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game else { auto OldPlaylist = GetPlaylistForOldVersion(); + SetupEverythingAI(); } auto Fortnite_Season = std::floor(Fortnite_Version); diff --git a/Project Reboot 3.0/FortItemDefinition.cpp b/Project Reboot 3.0/FortItemDefinition.cpp index f890d70..a2f6a4e 100644 --- a/Project Reboot 3.0/FortItemDefinition.cpp +++ b/Project Reboot 3.0/FortItemDefinition.cpp @@ -41,9 +41,9 @@ float UFortItemDefinition::GetMaxStackSize() for (auto& Pair : RowMap) { - if (Pair.Key() == ScalableFloat.Curve.RowName) + if (Pair.Key == ScalableFloat.Curve.RowName) { - Curve = (FSimpleCurve*)Pair.Value(); + Curve = (FSimpleCurve*)Pair.Value; break; } } diff --git a/Project Reboot 3.0/FortLootLevel.cpp b/Project Reboot 3.0/FortLootLevel.cpp index 2ab6794..a061763 100644 --- a/Project Reboot 3.0/FortLootLevel.cpp +++ b/Project Reboot 3.0/FortLootLevel.cpp @@ -22,10 +22,10 @@ int UFortLootLevel::GetItemLevel(const FDataTableCategoryHandle& LootLevelData, for (auto& LootLevelDataPair : LootLevelData.DataTable->GetRowMap()) { - if (LootLevelDataPair.Second->Category != LootLevelData.RowContents) + if (LootLevelDataPair.Value.Category != LootLevelData.RowContents) continue; - OurLootLevelDatas.push_back(LootLevelDataPair.Second); + OurLootLevelDatas.push_back(&LootLevelDataPair.Value); } if (OurLootLevelDatas.size() > 0) diff --git a/Project Reboot 3.0/FortLootPackage.cpp b/Project Reboot 3.0/FortLootPackage.cpp index ac67824..9edc5df 100644 --- a/Project Reboot 3.0/FortLootPackage.cpp +++ b/Project Reboot 3.0/FortLootPackage.cpp @@ -47,14 +47,16 @@ void CollectDataTablesRows(const std::vector& DataTables, LOOTING_M DataTablesToIterate.push_back(DataTable); } + return; // T(1) + for (auto CurrentDataTable : DataTablesToIterate) { - for (TPair& CurrentPair : CurrentDataTable->GetRowMap()) + for (auto& CurrentPair : CurrentDataTable->GetRowMap()) { - if (Check(CurrentPair.Key(), (RowStructType*)CurrentPair.Value())) + if (Check(CurrentPair.Key, (RowStructType*)CurrentPair.Value)) { // LOG_INFO(LogDev, "Setting key with {} comp {} num: {} then iterating through map!", CurrentPair.Key().ToString(), CurrentPair.Key().ComparisonIndex.Value, CurrentPair.Key().Number); - (*OutMap)[CurrentPair.Key()] = (RowStructType*)CurrentPair.Value(); + (*OutMap)[CurrentPair.Key] = (RowStructType*)CurrentPair.Value; /* for (auto PairInOutMap : *OutMap) { @@ -503,14 +505,14 @@ std::vector PickLootDrops(FName TierGroupName, int WorldLevel, int For for (auto& Value : PlaylistOverrideLootTableData) { - auto CurrentOverrideTag = Value.First; + auto CurrentOverrideTag = Value.Key; if (Tag.TagName == CurrentOverrideTag.TagName) { - auto OverrideLootPackageTableStr = Value.Second.LootPackageData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); + auto OverrideLootPackageTableStr = Value.Value.LootPackageData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); auto bOverrideIsComposite = OverrideLootPackageTableStr.contains("Composite"); - auto ptr = Value.Second.LootPackageData.Get(bOverrideIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true); + auto ptr = Value.Value.LootPackageData.Get(bOverrideIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true); if (ptr) { @@ -557,14 +559,14 @@ std::vector PickLootDrops(FName TierGroupName, int WorldLevel, int For for (auto& Value : PlaylistOverrideLootTableData) { - auto CurrentOverrideTag = Value.First; + auto CurrentOverrideTag = Value.Key; if (Tag.TagName == CurrentOverrideTag.TagName) { - auto OverrideLootTierDataStr = Value.Second.LootTierData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); + auto OverrideLootTierDataStr = Value.Value.LootTierData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); auto bOverrideIsComposite = OverrideLootTierDataStr.contains("Composite"); - auto ptr = Value.Second.LootTierData.Get(bOverrideIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true); + auto ptr = Value.Value.LootTierData.Get(bOverrideIsComposite ? CompositeDataTableClass : UDataTable::StaticClass(), true); if (ptr) { diff --git a/Project Reboot 3.0/FortPlayerController.cpp b/Project Reboot 3.0/FortPlayerController.cpp index a37b366..b8ccef1 100644 --- a/Project Reboot 3.0/FortPlayerController.cpp +++ b/Project Reboot 3.0/FortPlayerController.cpp @@ -567,6 +567,10 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame* FWeaponUpgradeItemRow* FoundRow = nullptr; + /* + + T(3) + for (int i = 0; i < LootPackagesRowMap.Pairs.Elements.Data.Num(); i++) { auto& Pair = LootPackagesRowMap.Pairs.Elements.Data.at(i).ElementData.Value; @@ -579,6 +583,8 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame* break; } } + + */ if (!FoundRow) { diff --git a/Project Reboot 3.0/FortWeaponItemDefinition.cpp b/Project Reboot 3.0/FortWeaponItemDefinition.cpp index 2f15387..99dfbea 100644 --- a/Project Reboot 3.0/FortWeaponItemDefinition.cpp +++ b/Project Reboot 3.0/FortWeaponItemDefinition.cpp @@ -17,6 +17,10 @@ int UFortWeaponItemDefinition::GetClipSize() void* Row = nullptr; + /* + + T(1) + for (int i = 0; i < RowMap.Pairs.Elements.Data.Num(); ++i) { auto& Pair = RowMap.Pairs.Elements.Data.at(i).ElementData.Value; @@ -28,6 +32,8 @@ int UFortWeaponItemDefinition::GetClipSize() } } + */ + if (!Row) return 0; diff --git a/Project Reboot 3.0/Level.cpp b/Project Reboot 3.0/Level.cpp index a206634..4375d4c 100644 --- a/Project Reboot 3.0/Level.cpp +++ b/Project Reboot 3.0/Level.cpp @@ -1,4 +1,5 @@ #include "Level.h" +#include "reboot.h" UWorld*& ULevel::GetOwningWorld() { @@ -6,7 +7,7 @@ UWorld*& ULevel::GetOwningWorld() return Get(OwningWorldOffset); } -bool ULevel::HasVisibilityChangeRequestPending() +bool ULevel::HasVisibilityChangeRequestPending() // T(REP) { // I believe implementation on this changes depending on the version @@ -24,6 +25,11 @@ bool ULevel::HasVisibilityChangeRequestPending() return this == CurrentLevelPendingVisibility || this == CurrentLevelPendingInvisibility; } +bool ULevel::IsAssociatingLevel() // T(REP) +{ + return false; +} + AWorldSettings* ULevel::GetWorldSettings(bool bChecked) const { if (bChecked) @@ -33,4 +39,10 @@ AWorldSettings* ULevel::GetWorldSettings(bool bChecked) const static auto WorldSettingsOffset = GetOffset("WorldSettings"); return Get(WorldSettingsOffset); +} + +UClass* ULevel::StaticClass() +{ + static auto Class = FindObject(L"/Script/Engine.Level"); + return Class; } \ No newline at end of file diff --git a/Project Reboot 3.0/Level.h b/Project Reboot 3.0/Level.h index 7cafb9c..16c49d6 100644 --- a/Project Reboot 3.0/Level.h +++ b/Project Reboot 3.0/Level.h @@ -7,5 +7,8 @@ class ULevel : public UObject public: UWorld*& GetOwningWorld(); bool HasVisibilityChangeRequestPending(); + bool IsAssociatingLevel(); AWorldSettings* GetWorldSettings(bool bChecked = true) const; + + static class UClass* StaticClass(); }; \ No newline at end of file diff --git a/Project Reboot 3.0/Map.h b/Project Reboot 3.0/Map.h index 2209080..484820a 100644 --- a/Project Reboot 3.0/Map.h +++ b/Project Reboot 3.0/Map.h @@ -1,6 +1,8 @@ #pragma once #include "Set.h" +#include "UnrealTemplate.h" +#include "ChooseClass.h" // template // using TPair = TTuple; @@ -9,151 +11,370 @@ template class TPair { public: - KeyType First; - ValueType Second; - - FORCEINLINE KeyType& Key() - { - return First; - } - FORCEINLINE const KeyType& Key() const - { - return First; - } - FORCEINLINE ValueType& Value() - { - return Second; - } - FORCEINLINE const ValueType& Value() const - { - return Second; - } + KeyType Key; + ValueType Value; }; -template -class TMap +template +class TPairInitializer { public: + typename TRValueToLValueReference::Type Key; + typename TRValueToLValueReference::Type Value; + + /** Initialization constructor. */ + FORCEINLINE TPairInitializer(KeyInitType InKey, ValueInitType InValue) + : Key(InKey) + , Value(InValue) + { + } + + /** Implicit conversion to pair initializer. */ + template + FORCEINLINE TPairInitializer(const TPair& Pair) + : Key(Pair.Key) + , Value(Pair.Value) + { + } + + template + operator TPair() const + { +#define StaticCast static_cast // Milxnor: ?? + return TPair(StaticCast(Key), StaticCast(Value)); + } +}; + +template +struct TDefaultMapKeyFuncs : BaseKeyFuncs, KeyType, bInAllowDuplicateKeys> +{ + typedef typename TTypeTraits::ConstPointerType KeyInitType; + typedef const TPairInitializer::ConstInitType, typename TTypeTraits::ConstInitType>& ElementInitType; + + static FORCEINLINE KeyInitType GetSetKey(ElementInitType Element) + { + return Element.Key; + } + static FORCEINLINE bool Matches(KeyInitType A, KeyInitType B) + { + return A == B; + } + static FORCEINLINE uint32 GetKeyHash(KeyInitType Key) + { + return GetTypeHash(Key); + } +}; + +template +struct TDefaultMapHashableKeyFuncs : TDefaultMapKeyFuncs +{ + // static_assert(THasGetTypeHash::Value, "TMap must have a hashable KeyType unless a custom key func is provided."); // T(R) +}; + +template +class TMapBase +{ + template + friend class TMapBase; + + friend struct TContainerTraits; + +public: + typedef typename TTypeTraits::ConstPointerType KeyConstPointerType; + typedef typename TTypeTraits::ConstInitType KeyInitType; + typedef typename TTypeTraits::ConstInitType ValueInitType; + typedef TPair ElementType; + +protected: + TMapBase() = default; + TMapBase(TMapBase&&) = default; + TMapBase(const TMapBase&) = default; + TMapBase& operator=(TMapBase&&) = default; + TMapBase& operator=(const TMapBase&) = default; + + typedef TSet ElementSetType; + + /** The base of TMapBase iterators. */ + template + class TBaseIterator + { + public: + typedef typename TChooseClass< + bConst, + typename TChooseClass::Result, + typename TChooseClass::Result + >::Result PairItType; + private: + typedef typename TChooseClass::Result MapType; + typedef typename TChooseClass::Result ItKeyType; + typedef typename TChooseClass::Result ItValueType; + typedef typename TChooseClass::Result PairType; + + public: + FORCEINLINE TBaseIterator(const PairItType& InElementIt) + : PairIt(InElementIt) + { + } + + FORCEINLINE TBaseIterator& operator++() + { + ++PairIt; + return *this; + } + + /** conversion to "bool" returning true if the iterator is valid. */ + FORCEINLINE explicit operator bool() const + { + return !!PairIt; + } + /** inverse of the "bool" operator */ + FORCEINLINE bool operator !() const + { + return !(bool)*this; + } + + FORCEINLINE friend bool operator==(const TBaseIterator& Lhs, const TBaseIterator& Rhs) { return Lhs.PairIt == Rhs.PairIt; } + FORCEINLINE friend bool operator!=(const TBaseIterator& Lhs, const TBaseIterator& Rhs) { return Lhs.PairIt != Rhs.PairIt; } + + FORCEINLINE ItKeyType& Key() const { return PairIt->Key; } + FORCEINLINE ItValueType& Value() const { return PairIt->Value; } + + FORCEINLINE PairType& operator* () const { return *PairIt; } + FORCEINLINE PairType* operator->() const { return &*PairIt; } + + protected: + PairItType PairIt; + }; + + /** The base type of iterators that iterate over the values associated with a specified key. */ + template + class TBaseKeyIterator + { + private: + typedef typename TChooseClass::Result SetItType; + typedef typename TChooseClass::Result ItKeyType; + typedef typename TChooseClass::Result ItValueType; + + public: + /** Initialization constructor. */ + FORCEINLINE TBaseKeyIterator(const SetItType& InSetIt) + : SetIt(InSetIt) + { + } + + FORCEINLINE TBaseKeyIterator& operator++() + { + ++SetIt; + return *this; + } + + /** conversion to "bool" returning true if the iterator is valid. */ + FORCEINLINE explicit operator bool() const + { + return !!SetIt; + } + /** inverse of the "bool" operator */ + FORCEINLINE bool operator !() const + { + return !(bool)*this; + } + + FORCEINLINE ItKeyType& Key() const { return SetIt->Key; } + FORCEINLINE ItValueType& Value() const { return SetIt->Value; } + + protected: + SetItType SetIt; + }; + + /** A set of the key-value pairs in the map. */ + ElementSetType Pairs; + +public: + + /** Map iterator. */ + class TIterator : public TBaseIterator + { + public: + + /** Initialization constructor. */ + FORCEINLINE TIterator(TMapBase& InMap, bool bInRequiresRehashOnRemoval = false) + : TBaseIterator(InMap.Pairs.CreateIterator()) + , Map(InMap) + , bElementsHaveBeenRemoved(false) + , bRequiresRehashOnRemoval(bInRequiresRehashOnRemoval) + { + } + + /** Destructor. */ + FORCEINLINE ~TIterator() + { + if (bElementsHaveBeenRemoved && bRequiresRehashOnRemoval) + { + Map.Pairs.Relax(); + } + } + + /** Removes the current pair from the map. */ + FORCEINLINE void RemoveCurrent() + { + TBaseIterator::PairIt.RemoveCurrent(); + bElementsHaveBeenRemoved = true; + } + + private: + TMapBase& Map; + bool bElementsHaveBeenRemoved; + bool bRequiresRehashOnRemoval; + }; + + /** Const map iterator. */ + class TConstIterator : public TBaseIterator + { + public: + FORCEINLINE TConstIterator(const TMapBase& InMap) + : TBaseIterator(InMap.Pairs.CreateConstIterator()) + { + } + }; + + using TRangedForIterator = TBaseIterator; + using TRangedForConstIterator = TBaseIterator; + + /** Iterates over values associated with a specified key in a const map. */ + class TConstKeyIterator : public TBaseKeyIterator + { + public: + FORCEINLINE TConstKeyIterator(const TMapBase& InMap, KeyInitType InKey) + : TBaseKeyIterator(typename ElementSetType::TConstKeyIterator(InMap.Pairs, InKey)) + {} + }; + + /** Iterates over values associated with a specified key in a map. */ + class TKeyIterator : public TBaseKeyIterator + { + public: + FORCEINLINE TKeyIterator(TMapBase& InMap, KeyInitType InKey) + : TBaseKeyIterator(typename ElementSetType::TKeyIterator(InMap.Pairs, InKey)) + {} + + /** Removes the current key-value pair from the map. */ + FORCEINLINE void RemoveCurrent() + { + TBaseKeyIterator::SetIt.RemoveCurrent(); + } + }; + + /** Creates an iterator over all the pairs in this map */ + FORCEINLINE TIterator CreateIterator() + { + return TIterator(*this); + } + + /** Creates a const iterator over all the pairs in this map */ + FORCEINLINE TConstIterator CreateConstIterator() const + { + return TConstIterator(*this); + } + + /** Creates an iterator over the values associated with a specified key in a map */ + FORCEINLINE TKeyIterator CreateKeyIterator(KeyInitType InKey) + { + return TKeyIterator(*this, InKey); + } + + /** Creates a const iterator over the values associated with a specified key in a map */ + FORCEINLINE TConstKeyIterator CreateConstKeyIterator(KeyInitType InKey) const + { + return TConstKeyIterator(*this, InKey); + } + + FORCEINLINE ValueType* Find(KeyConstPointerType Key) + { + if (auto* Pair = Pairs.Find(Key)) + { + return &Pair->Value; + } + + return nullptr; + } + + FORCEINLINE const ValueType& FindChecked(KeyConstPointerType Key) const + { + const auto* Pair = Pairs.Find(Key); + // check(Pair != nullptr); + return Pair->Value; + } + + /** + * Find a reference to the value associated with a specified key. + * + * @param Key The key to search for. + * @return The value associated with the specified key, or triggers an assertion if the key does not exist. + */ + FORCEINLINE ValueType& FindChecked(KeyConstPointerType Key) + { + auto* Pair = Pairs.Find(Key); + // check(Pair != nullptr); + return Pair->Value; + } + + FORCEINLINE ValueType FindRef(KeyConstPointerType Key) const + { + if (const auto* Pair = Pairs.Find(Key)) + { + return Pair->Value; + } + + return ValueType(); + } + + FORCEINLINE int32 Num() const + { + return Pairs.Num(); + } + +private: + /** + * DO NOT USE DIRECTLY + * STL-like iterators to enable range-based for loop support. + */ + FORCEINLINE friend TRangedForIterator begin(TMapBase& MapBase) { return TRangedForIterator(begin(MapBase.Pairs)); } + FORCEINLINE friend TRangedForConstIterator begin(const TMapBase& MapBase) { return TRangedForConstIterator(begin(MapBase.Pairs)); } + FORCEINLINE friend TRangedForIterator end(TMapBase& MapBase) { return TRangedForIterator(end(MapBase.Pairs)); } + FORCEINLINE friend TRangedForConstIterator end(const TMapBase& MapBase) { return TRangedForConstIterator(end(MapBase.Pairs)); } +}; + +template +class TSortableMapBase : public TMapBase +{ + // friend struct TContainerTraits; + +protected: + typedef TMapBase Super; + + TSortableMapBase() = default; + TSortableMapBase(TSortableMapBase&&) = default; + TSortableMapBase(const TSortableMapBase&) = default; + TSortableMapBase& operator=(TSortableMapBase&&) = default; + TSortableMapBase& operator=(const TSortableMapBase&) = default; +}; + +template> +class TMap : public TSortableMapBase +{ +public: + typedef TSortableMapBase Super; + typedef typename Super::KeyInitType KeyInitType; + typedef typename Super::KeyConstPointerType KeyConstPointerType; + typedef TPair ElementType; +public: + typedef TSet ElementSetType; public: - TSet Pairs; - -public: - class FBaseIterator - { - private: - TMap& IteratedMap; - TSet::FBaseIterator SetIt; - - public: - FBaseIterator(TMap& InMap, TSet::FBaseIterator InSet) - : IteratedMap(InMap) - , SetIt(InSet) - { - } - FORCEINLINE TMap::FBaseIterator operator++() - { - ++SetIt; - return *this; - } - FORCEINLINE TMap::ElementType& operator*() - { - return *SetIt; - } - FORCEINLINE const TMap::ElementType& operator*() const - { - return *SetIt; - } - FORCEINLINE bool operator==(const TMap::FBaseIterator& Other) const - { - return SetIt == Other.SetIt; - } - FORCEINLINE bool operator!=(const TMap::FBaseIterator& Other) const - { - return SetIt != Other.SetIt; - } - FORCEINLINE bool IsElementValid() const - { - return SetIt.IsElementValid(); - } - }; - - FORCEINLINE TMap::FBaseIterator begin() - { - return TMap::FBaseIterator(*this, Pairs.begin()); - } - FORCEINLINE const TMap::FBaseIterator begin() const - { - return TMap::FBaseIterator(*this, Pairs.begin()); - } - FORCEINLINE TMap::FBaseIterator end() - { - return TMap::FBaseIterator(*this, Pairs.end()); - } - FORCEINLINE const TMap::FBaseIterator end() const - { - return TMap::FBaseIterator(*this, Pairs.end()); - } - FORCEINLINE ValueType& operator[](const KeyType& Key) - { - return this->GetByKey(Key); - } - FORCEINLINE const ValueType& operator[](const KeyType& Key) const - { - return this->GetByKey(Key); - } - FORCEINLINE int32 Num() const - { - return Pairs.Num(); - } - FORCEINLINE bool IsValid() const - { - return Pairs.IsValid(); - } - FORCEINLINE bool IsIndexValid(int32 IndexToCheck) const - { - return Pairs.IsIndexValid(IndexToCheck); - } - FORCEINLINE bool Contains(const KeyType& ElementToLookFor) const - { - for (auto& Element : *this) - { - if (Element.Key() == ElementToLookFor) - return true; - } - return false; - } - FORCEINLINE ValueType& GetByKey(const KeyType& Key, bool* wasSuccessful = nullptr) - { - for (auto& Pair : *this) - { - if (Pair.Key() == Key) - { - if (wasSuccessful) - *wasSuccessful = true; - - return Pair.Value(); - } - } - - // LOG_INFO(LogDev, "Failed to find Key!!!"); - - if (wasSuccessful) - *wasSuccessful = false; - } - FORCEINLINE ValueType& Find(const KeyType& Key, bool* wasSuccessful = nullptr) - { - return GetByKey(Key, wasSuccessful); - } - FORCEINLINE ValueType GetByKeyNoRef(const KeyType& Key) - { - for (auto& Pair : *this) - { - if (Pair.Key() == Key) - { - return Pair.Value(); - } - } - } + TMap() = default; + TMap(TMap&&) = default; + TMap(const TMap&) = default; + TMap& operator=(TMap&&) = default; + TMap& operator=(const TMap&) = default; }; \ No newline at end of file diff --git a/Project Reboot 3.0/NameTypes.h b/Project Reboot 3.0/NameTypes.h index 68bde9d..ce24c12 100644 --- a/Project Reboot 3.0/NameTypes.h +++ b/Project Reboot 3.0/NameTypes.h @@ -76,4 +76,11 @@ struct FName return GetComparisonIndexFast() < Rhs.GetComparisonIndexFast(); } -}; \ No newline at end of file +}; + +inline uint32 GetTypeHash(const FName N) +{ + return N.ComparisonIndex.Value + N.GetNumber(); +} + +#define NAME_None FName(0); \ No newline at end of file diff --git a/Project Reboot 3.0/NetConnection.h b/Project Reboot 3.0/NetConnection.h index 43db1cc..339e523 100644 --- a/Project Reboot 3.0/NetConnection.h +++ b/Project Reboot 3.0/NetConnection.h @@ -6,10 +6,37 @@ #include "WeakObjectPtrTemplates.h" #include "ActorChannel.h" #include +#include "KismetStringLibrary.h" class UNetConnection : public UPlayer { public: + static inline UChannel* (*originalCreateChannel)(UNetConnection*, int, bool, int32_t); + static inline UChannel* (*originalCreateChannelByName)(UNetConnection* Connection, FName* ChName, EChannelCreateFlags CreateFlags, int32_t ChannelIndex); + + TSet& GetDestroyedStartupOrDormantActors() // T(REP) + { + static int off = Fortnite_Version == 1.11 ? 0x33678 : 0; + + return *(TSet*)(__int64(this) + off); + } + + UChannel* CreateChannel(EChannelType ChannelType, bool bOpenedLocally, EChannelCreateFlags CreateFlags, int32 ChannelIndex = INDEX_NONE) + { + if (Engine_Version >= 422) + { + FString ActorStr = L"Actor"; + FName ActorName = UKismetStringLibrary::Conv_StringToName(ActorStr); + + int ChannelIndex = -1; // 4294967295 + return (UActorChannel*)originalCreateChannelByName(this, &ActorName, CreateFlags, ChannelIndex); + } + else + { + return (UActorChannel*)originalCreateChannel(this, ChannelType, bOpenedLocally, ChannelIndex); + } + } + AActor*& GetOwningActor() { static auto OwningActorOffset = GetOffset("OwningActor"); @@ -22,13 +49,24 @@ public: return *(FName*)(__int64(this) + ClientWorldPackageNameOffset); } - AActor*& GetViewTarget() + inline AActor*& GetViewTarget() { static auto ViewTargetOffset = GetOffset("ViewTarget"); return Get(ViewTargetOffset); } - int32 IsNetReady(bool Saturate) + bool& GetTimeSensitive() // T(REP) + { + return *(bool*)(0); + } + + TArray& GetChildren() + { + static auto ChildrenOffset = GetOffset("Children"); + return Get>(ChildrenOffset); + } + + int32 IsNetReady(bool Saturate) // T(REP) { static auto IsNetReadyOffset = 0x298; // 1.11 int32 (*IsNetReadyOriginal)(UNetConnection* Connection, bool Saturate) = decltype(IsNetReadyOriginal)(this->VFTable[IsNetReadyOffset / 8]); diff --git a/Project Reboot 3.0/NetDriver.cpp b/Project Reboot 3.0/NetDriver.cpp index d539e2a..c4449e0 100644 --- a/Project Reboot 3.0/NetDriver.cpp +++ b/Project Reboot 3.0/NetDriver.cpp @@ -7,28 +7,13 @@ #include "GameplayStatics.h" #include "KismetMathLibrary.h" #include -#include "Package.h"S +#include "Package.h" #include "AssertionMacros.h" +#include "ChildConnection.h" #include "bots.h" +#include "NetworkingDistanceConstants.h" #include "gui.h" -FNetworkObjectList& UNetDriver::GetNetworkObjectList() -{ - return *(*(TSharedPtr*)(__int64(this) + Offsets::NetworkObjectList)); -} - -bool ShouldUseNetworkObjectList() -{ - return Fortnite_Version < 20; -} - -void UNetDriver::RemoveNetworkActor(AActor* Actor) -{ - GetNetworkObjectList().Remove(Actor); - - // RenamedStartupActors.Remove(Actor->GetFName()); -} - void UNetDriver::TickFlushHook(UNetDriver* NetDriver) { if (bShouldDestroyAllPlayerBuilds) // i hate this @@ -78,362 +63,65 @@ void UNetDriver::TickFlushHook(UNetDriver* NetDriver) return TickFlushOriginal(NetDriver); } -enum class EChannelCreateFlags : uint32_t +/* + +Replication from Unreal Engine 4.19 (using std::vector instead of TArrays). + +Can we make it faster? Perhaps removing references from some gets, but it shouldn't do crazy. + +*/ + +FNetworkObjectList& UNetDriver::GetNetworkObjectList() { - None = (1 << 0), - OpenedLocally = (1 << 1) -}; + return *(*(TSharedPtr*)(__int64(this) + Offsets::NetworkObjectList)); +} -int32 ServerReplicateActors_PrepConnections(UNetDriver* NetDriver) +void UNetDriver::RemoveNetworkActor(AActor* Actor) // T(REP) { - auto& ClientConnections = NetDriver->GetClientConnections(); + GetNetworkObjectList().Remove(Actor); - int32 NumClientsToTick = ClientConnections.Num(); + // RenamedStartupActors.Remove(Actor->GetFName()); +} - bool bFoundReadyConnection = false; - for (int32 ConnIdx = 0; ConnIdx < ClientConnections.Num(); ConnIdx++) +FActorPriority::FActorPriority(UNetConnection* InConnection, UActorChannel* InChannel, FNetworkObjectInfo* InActorInfo, const std::vector& Viewers, bool bLowBandwidth) + : ActorInfo(InActorInfo), Channel(InChannel), DestructionInfo(NULL) +{ + float Time = Channel ? (InConnection->GetDriver()->GetTime() - Channel->GetLastUpdateTime()) : InConnection->GetDriver()->GetSpawnPrioritySeconds(); + // take the highest priority of the viewers on this connection + Priority = 0; + for (int32 i = 0; i < Viewers.size(); i++) { - UNetConnection* Connection = ClientConnections.at(ConnIdx); - if (!Connection) continue; - // check(Connection->State == USOCK_Pending || Connection->State == USOCK_Open || Connection->State == USOCK_Closed); - // checkSlow(Connection->GetUChildConnection() == NULL); + // Priority = FMath::Max(Priority, FMath::RoundToInt(65536.0f * + // ActorInfo->Actor->GetNetPriority(Viewers[i].ViewLocation, Viewers[i].ViewDir, Viewers[i].InViewer, V[i].ViewTarget, InChannel, Time, bLowBandwidth))); + } +} - AActor* OwningActor = Connection->GetOwningActor(); +FActorPriority::FActorPriority(class UNetConnection* InConnection, struct FActorDestructionInfo* Info, const std::vector& Viewers) + : ActorInfo(NULL), Channel(NULL), DestructionInfo(Info) +{ + Priority = 0; - if (OwningActor != NULL) // && /* Connection->State == USOCK_Open && */ (Connection->Driver->Time - Connection->LastReceiveTime < 1.5f)) + for (int32 i = 0; i < Viewers.size(); i++) + { + float Time = InConnection->GetDriver()->GetSpawnPrioritySeconds(); + + FVector Dir = DestructionInfo->DestroyedPosition - Viewers[i].ViewLocation; + float DistSq = Dir.SizeSquared(); + + // adjust priority based on distance and whether actor is in front of viewer + if ((Viewers[i].ViewDir | Dir) < 0.f) { - bFoundReadyConnection = true; - - AActor* DesiredViewTarget = OwningActor; - - if (Connection->GetPlayerController()) - { - if (AActor* ViewTarget = Connection->GetPlayerController()->GetViewTarget()) - { - DesiredViewTarget = ViewTarget; - } - } - - Connection->GetViewTarget() = DesiredViewTarget; - } - else - { - Connection->GetViewTarget() = NULL; + if (DistSq > NEARSIGHTTHRESHOLDSQUARED) + Time *= 0.2f; + else if (DistSq > CLOSEPROXIMITYSQUARED) + Time *= 0.4f; } + else if (DistSq > MEDSIGHTTHRESHOLDSQUARED) + Time *= 0.4f; + + Priority = FMath::Max(Priority, 65536.0f * Time); } - - return bFoundReadyConnection ? NumClientsToTick : 0; -} - -enum class ENetRole : uint8_t -{ - ROLE_None = 0, - ROLE_SimulatedProxy = 1, - ROLE_AutonomousProxy = 2, - ROLE_Authority = 3, - ROLE_MAX = 4 -}; - -FORCEINLINE float FRand() -{ - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution<> dis(0, 1); - float random_number = dis(gen); - - return random_number; -} - -static FORCEINLINE bool ShouldActorGoDormant(AActor* Actor, const std::vector& ConnectionViewers, UActorChannel* Channel, const float Time, const bool bLowNetBandwidth) -{ - using enum ENetDormancy; - - static auto bPendingDormancyOffset = 0x30; - static auto bPendingDormancyFieldMask = 0x0; - static auto DormantOffset = 0x30; - static auto DormantFieldMask = 0x0; - - if (Actor->GetNetDormancy() <= DORM_Awake || !Channel - // || ReadBitfield((PlaceholderBitfield*)(__int64(Channel) + bPendingDormancyOffset), bPendingDormancyFieldMask) - // || ReadBitfield((PlaceholderBitfield*)(__int64(Channel) + DormantOffset), DormantFieldMask) - || Channel->IsPendingDormancy() - || Channel->IsDormant() - ) - { - // Either shouldn't go dormant, or is already dormant - return false; - } - - if (Actor->GetNetDormancy() == DORM_DormantPartial) - { - for (int32 viewerIdx = 0; viewerIdx < ConnectionViewers.size(); viewerIdx++) - { - // if (!Actor->GetNetDormancy(ConnectionViewers[viewerIdx].ViewLocation, ConnectionViewers[viewerIdx].ViewDir, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, Channel, Time, bLowNetBandwidth)) - if (!false) // ^ this just returns false soo (atleast AActor implementation) - { - return false; - } - } - } - - return true; -} - -void UNetDriver::ServerReplicateActors_BuildConsiderList(std::vector& OutConsiderList, float ServerTickTime) -{ - std::vector ActorsToRemove; - - if (ShouldUseNetworkObjectList()) - { - auto& ActiveObjects = GetNetworkObjectList().ActiveNetworkObjects; - - auto World = GetWorld(); - - for (const TSharedPtr& ActorInfo : ActiveObjects) - { - if (!ActorInfo->bPendingNetUpdate && UGameplayStatics::GetTimeSeconds(GetWorld()) <= ActorInfo->NextUpdateTime) - { - continue; - } - - auto Actor = ActorInfo->Actor; - - if (!Actor) - continue; - - if (Actor->IsPendingKillPending()) - // if (Actor->IsPendingKill()) - { - ActorsToRemove.push_back(Actor); - continue; - } - - static auto RemoteRoleOffset = Actor->GetOffset("RemoteRole"); - - if (Actor->Get(RemoteRoleOffset) == ENetRole::ROLE_None) - { - ActorsToRemove.push_back(Actor); - continue; - } - - // We should add a NetDriverName check but I don't believe it is needed. - - // We should check if the actor is initialized here. - - // We should check the level stuff here. - - static auto NetDormancyOffset = Actor->GetOffset("NetDormancy"); - - if (Actor->Get(NetDormancyOffset) == ENetDormancy::DORM_Initial && Actor->IsNetStartupActor()) // IsDormInitialStartupActor - { - continue; - } - - // We should check NeedsLoadForClient here. - // We should make sure the actor is in the same world here but I don't believe it is needed. - - if (ActorInfo->LastNetReplicateTime == 0) - { - ActorInfo->LastNetReplicateTime = UGameplayStatics::GetTimeSeconds(World); - ActorInfo->OptimalNetUpdateDelta = 1.0f / Actor->GetNetUpdateFrequency(); - } - - const float ScaleDownStartTime = 2.0f; - const float ScaleDownTimeRange = 5.0f; - - const float LastReplicateDelta = UGameplayStatics::GetTimeSeconds(World) - ActorInfo->LastNetReplicateTime; - - if (LastReplicateDelta > ScaleDownStartTime) - { - static auto MinNetUpdateFrequencyOffset = Actor->GetOffset("MinNetUpdateFrequency"); - - if (Actor->Get(MinNetUpdateFrequencyOffset) == 0.0f) - { - Actor->Get(MinNetUpdateFrequencyOffset) = 2.0f; - } - - const float MinOptimalDelta = 1.0f / Actor->GetNetUpdateFrequency(); // Don't go faster than NetUpdateFrequency - const float MaxOptimalDelta = max(1.0f / Actor->GetMinNetUpdateFrequency(), MinOptimalDelta); // Don't go slower than MinNetUpdateFrequency (or NetUpdateFrequency if it's slower) - - const float Alpha = std::clamp((LastReplicateDelta - ScaleDownStartTime) / ScaleDownTimeRange, 0.0f, 1.0f); // should we use fmath? - ActorInfo->OptimalNetUpdateDelta = std::lerp(MinOptimalDelta, MaxOptimalDelta, Alpha); // should we use fmath? - } - - if (!ActorInfo->bPendingNetUpdate) - { - constexpr bool bUseAdapativeNetFrequency = false; - const float NextUpdateDelta = bUseAdapativeNetFrequency ? ActorInfo->OptimalNetUpdateDelta : 1.0f / Actor->GetNetUpdateFrequency(); - - // then set the next update time - float ServerTickTime = 1.f / 30; - ActorInfo->NextUpdateTime = UGameplayStatics::GetTimeSeconds(World) + FRand() * ServerTickTime + NextUpdateDelta; - static auto TimeOffset = GetOffset("Time"); - ActorInfo->LastNetUpdateTime = Get(TimeOffset); - } - - ActorInfo->bPendingNetUpdate = false; - - OutConsiderList.push_back(ActorInfo.Get()); - - static void (*CallPreReplication)(AActor*, UNetDriver*) = decltype(CallPreReplication)(Addresses::CallPreReplication); - CallPreReplication(Actor, this); - } - } - else - { - auto Actors = UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass()); - - for (int i = 0; i < Actors.Num(); ++i) - { - auto Actor = Actors.at(i); - - if (Actor->IsPendingKillPending()) - // if (Actor->IsPendingKill()) - { - ActorsToRemove.push_back(Actor); - continue; - } - - static auto RemoteRoleOffset = Actor->GetOffset("RemoteRole"); - - if (Actor->Get(RemoteRoleOffset) == ENetRole::ROLE_None) - { - ActorsToRemove.push_back(Actor); - continue; - } - - // We should add a NetDriverName check but I don't believe it is needed. - - // We should check if the actor is initialized here. - - // We should check the level stuff here. - - static auto NetDormancyOffset = Actor->GetOffset("NetDormancy"); - - if (Actor->Get(NetDormancyOffset) == ENetDormancy::DORM_Initial && Actor->IsNetStartupActor()) // IsDormInitialStartupActor - { - continue; - } - - auto ActorInfo = new FNetworkObjectInfo; - ActorInfo->Actor = Actor; - - OutConsiderList.push_back(ActorInfo); - - static void (*CallPreReplication)(AActor*, UNetDriver*) = decltype(CallPreReplication)(Addresses::CallPreReplication); - CallPreReplication(Actor, this); - } - - Actors.Free(); - } - - for (auto Actor : ActorsToRemove) - { - if (!Actor) - continue; - - /* LOG_INFO(LogDev, "Removing actor: {}", Actor ? Actor->GetFullName() : "InvalidObject"); - RemoveNetworkActor(Actor); - LOG_INFO(LogDev, "Finished removing actor."); */ - } -} - -static UActorChannel* FindChannel(AActor * Actor, UNetConnection * Connection) -{ - static auto OpenChannelsOffset = Connection->GetOffset("OpenChannels"); - auto& OpenChannels = Connection->Get>(OpenChannelsOffset); - - static auto ActorChannelClass = FindObject(L"/Script/Engine.ActorChannel"); - - // LOG_INFO(LogReplication, "OpenChannels.Num(): {}", OpenChannels.Num()); - - for (int i = 0; i < OpenChannels.Num(); ++i) - { - auto Channel = OpenChannels.at(i); - - if (!Channel) - continue; - - // LOG_INFO(LogReplication, "[{}] Class {}", i, Channel->ClassPrivate ? Channel->ClassPrivate->GetFullName() : "InvalidObject"); - - if (!Channel->IsA(ActorChannelClass)) // (Channel->ClassPrivate == ActorChannelClass) - continue; - - static auto ActorOffset = Channel->GetOffset("Actor"); - auto ChannelActor = Channel->Get(ActorOffset); - - // LOG_INFO(LogReplication, "[{}] {}", i, ChannelActor->GetFullName()); - - if (ChannelActor != Actor) - continue; - - return (UActorChannel*)Channel; - } - - // LOG_INFO(LogDev, "Failed to find channel for {}!", Actor->GetName()); - - return nullptr; -} - -static bool IsActorRelevantToConnection(AActor * Actor, std::vector&ConnectionViewers) -{ - for (int32 viewerIdx = 0; viewerIdx < ConnectionViewers.size(); viewerIdx++) - { - if (!ConnectionViewers[viewerIdx].ViewTarget) - continue; - - // static bool (*IsNetRelevantFor)(AActor*, AActor*, AActor*, FVector&) = decltype(IsNetRelevantFor)(__int64(GetModuleHandleW(0)) + 0x1ECC700); - - static auto index = Offsets::IsNetRelevantFor; - - // if (Actor->IsNetRelevantFor(ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) - // if (IsNetRelevantFor(Actor, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) - if (reinterpret_cast(Actor->VFTable[index])( - Actor, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) - { - return true; - } - } - - return false; -} - -static FNetViewer ConstructNetViewer(UNetConnection* NetConnection) -{ - FNetViewer newViewer{}; - newViewer.Connection = NetConnection; - newViewer.InViewer = NetConnection->GetPlayerController() ? NetConnection->GetPlayerController() : NetConnection->GetOwningActor(); - newViewer.ViewTarget = NetConnection->GetViewTarget(); - - if (!NetConnection->GetOwningActor() || !(!NetConnection->GetPlayerController() || (NetConnection->GetPlayerController() == NetConnection->GetOwningActor()))) - return newViewer; - - APlayerController* ViewingController = NetConnection->GetPlayerController(); - - newViewer.ViewLocation = newViewer.ViewTarget->GetActorLocation(); - - if (ViewingController) - { - static auto ControlRotationOffset = ViewingController->GetOffset("ControlRotation"); - FRotator ViewRotation = ViewingController->Get(ControlRotationOffset); // hmmmm // ViewingController->GetControlRotation(); - // AFortPlayerControllerAthena::GetPlayerViewPointHook(Cast(ViewingController, false), newViewer.ViewLocation, ViewRotation); - ViewingController->GetActorEyesViewPoint(&newViewer.ViewLocation, &ViewRotation); // HMMM - - // static auto GetActorEyesViewPointOffset = 0x5B0; - // void (*GetActorEyesViewPointOriginal)(AController*, FVector * a2, FRotator * a3) = decltype(GetActorEyesViewPointOriginal)(ViewingController->VFTable[GetActorEyesViewPointOffset / 8]); - // GetActorEyesViewPointOriginal(ViewingController, &newViewer.ViewLocation, &ViewRotation); - // AFortPlayerControllerAthena::GetPlayerViewPointHook((AFortPlayerControllerAthena*)ViewingController, newViewer.ViewLocation, ViewRotation); - newViewer.ViewDir = ViewRotation.Vector(); - } - - return newViewer; -} - -static FORCEINLINE bool IsActorDormant(FNetworkObjectInfo* ActorInfo, const TWeakObjectPtr& Connection) -{ - // If actor is already dormant on this channel, then skip replication entirely - return ActorInfo->DormantConnections.Contains(Connection); } bool UNetDriver::IsLevelInitializedForActor(const AActor* InActor, const UNetConnection* InConnection) const @@ -443,14 +131,14 @@ bool UNetDriver::IsLevelInitializedForActor(const AActor* InActor, const UNetCon return true; } -/* #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // (Milxnor) This is on some ue versions and others not. - if (!InActor || !InConnection) - return false; + /* #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // (Milxnor) This is on some ue versions and others not. + if (!InActor || !InConnection) + return false; - // check(World == InActor->GetWorld()); -#endif */ + // check(World == InActor->GetWorld()); + #endif */ - bool bFirstWorldCheck = Engine_Version == 416 + bool bFirstWorldCheck = Engine_Version == 416 ? (InConnection->GetClientWorldPackageName() == GetWorld()->GetOutermost()->GetFName()) : (InConnection->GetClientWorldPackageName() == GetWorldPackage()->NamePrivate); @@ -459,131 +147,566 @@ bool UNetDriver::IsLevelInitializedForActor(const AActor* InActor, const UNetCon return bCorrectWorld || bIsConnectionPC; } -TMap* GetDestroyedStartupOrDormantActors(UNetDriver* Driver) +FNetViewer::FNetViewer(UNetConnection* InConnection, float DeltaSeconds) : + Connection(InConnection), + InViewer(InConnection->GetPlayerController() ? InConnection->GetPlayerController() : InConnection->GetOwningActor()), + ViewTarget(InConnection->GetViewTarget()), + ViewLocation(ForceInit), + ViewDir(ForceInit) { - static int off = Fortnite_Version == 1.11 ? 0x228 : 0; // 0x240 + // check(InConnection->OwningActor); + // check(!InConnection->PlayerController || (InConnection->PlayerController == InConnection->OwningActor)); - return off == 0 ? nullptr : (TMap*)(__int64(Driver) + off); + APlayerController* ViewingController = InConnection->GetPlayerController(); + + // Get viewer coordinates. + ViewLocation = ViewTarget->GetActorLocation(); + if (ViewingController) + { + FRotator ViewRotation = ViewingController->GetControlRotation(); + // ViewingController->GetPlayerViewPoint(ViewLocation, ViewRotation); + ViewDir = ViewRotation.Vector(); + } } -TSet* GetDestroyedStartupOrDormantActors(UNetConnection* NetConnection) +static FORCEINLINE bool IsActorRelevantToConnection(const AActor* Actor, const std::vector& ConnectionViewers) { - static int off = Fortnite_Version == 1.11 ? 0x33678 : 0; + for (int32 viewerIdx = 0; viewerIdx < ConnectionViewers.size(); viewerIdx++) + { + if (reinterpret_cast(Actor->VFTable[Offsets::IsNetRelevantFor])( + Actor, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) + { + return true; + } + } - return off == 0 ? nullptr : (TSet*)(__int64(NetConnection) + off); -} - -using FArchive = void; - -bool IsError(FArchive* Ar) -{ return false; } -void SerializeChecksum(FArchive* Ar, uint32 x, bool ErrorOK) +static FORCEINLINE UNetConnection* IsActorOwnedByAndRelevantToConnection(const AActor* Actor, const std::vector& ConnectionViewers, bool& bOutHasNullViewTarget) { - /* - if (Ar->IsLoading()) + const AActor* ActorOwner = Actor->GetNetOwner(); + + bOutHasNullViewTarget = false; + + for (int i = 0; i < ConnectionViewers.size(); i++) { - uint32 Magic = 0; - Ar << Magic; - if ((!ErrorOK || !IsError(Ar)) - // && !ensure(Magic == x) - ) + UNetConnection* ViewerConnection = ConnectionViewers[i].Connection; + + auto ViewTarget = ViewerConnection->GetViewTarget(); + + if (ViewTarget == nullptr) { - // UE_LOG(LogCoreNet, Warning, TEXT("%d == %d"), Magic, x); + bOutHasNullViewTarget = true; } + if (ActorOwner == ViewerConnection->GetPlayerController() || + (ViewerConnection->GetPlayerController() && ActorOwner == ViewerConnection->GetPlayerController()->GetPawn()) || + (ViewTarget && ViewTarget->IsRelevancyOwnerFor(Actor, ActorOwner, ViewerConnection->GetOwningActor()))) + { + return ViewerConnection; + } } - else - { - uint32 Magic = x; - Ar << Magic; - } - */ + + return nullptr; } -#define NET_CHECKSUM(Ser) \ -{ \ - SerializeChecksum(Ser,0xE282FA84, false); \ +static FORCEINLINE bool IsActorDormant(FNetworkObjectInfo* ActorInfo, const TWeakObjectPtr& Connection) +{ + return ActorInfo->DormantConnections.Contains(Connection); } -struct FPacketIdRange +static FORCEINLINE bool ShouldActorGoDormant(AActor* Actor, const std::vector& ConnectionViewers, UActorChannel* Channel, const float Time, const bool bLowNetBandwidth) { - FPacketIdRange(int32 _First, int32 _Last) : First(_First), Last(_Last) { } - FPacketIdRange(int32 PacketId) : First(PacketId), Last(PacketId) { } - FPacketIdRange() : First(INDEX_NONE), Last(INDEX_NONE) { } - int32 First; - int32 Last; - - bool InRange(int32 PacketId) const + if (Actor->NetDormancy() <= ENetDormancy::DORM_Awake || !Channel || Channel->IsPendingDormancy() || Channel->IsDormant()) { - return (First <= PacketId && PacketId <= Last); + return false; } -}; -void SetChannelActorForDestroy(UActorChannel* Channel, FActorDestructionInfo* DestructInfo) + if (Actor->NetDormancy() == ENetDormancy::DORM_DormantPartial) + { + for (int32 viewerIdx = 0; viewerIdx < ConnectionViewers.size(); viewerIdx++) + { + if (!Actor->GetNetDormancy(ConnectionViewers[viewerIdx].ViewLocation, ConnectionViewers[viewerIdx].ViewDir, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, Channel, Time, bLowNetBandwidth)) + { + return false; + } + } + } + + return true; +} + +int32 UNetDriver::ServerReplicateActors_PrepConnections() { - auto Connection = Channel->GetConnection(); + int32 NumClientsToTick = GetClientConnections().Num(); - if ( - true - // && !Channel->IsClosing() - // && (Connection->State == USOCK_Open || Connection->State == USOCK_Pending) + /* + static bool bForceClientTickingThrottle = false; + if (bForceClientTickingThrottle + // || GetNetMode() == NM_ListenServer ) { + static float DeltaTimeOverflow = 0.f; - // Send a close notify, and wait for ack. - struct FOutBunch + static bool LanPlay = false; // FParse::Param(FCommandLine::Get(), TEXT("lanplay")); + float ClientUpdatesThisFrame = GetEngine()->NetClientTicksPerSecond * (DeltaSeconds + DeltaTimeOverflow) * (LanPlay ? 2.f : 1.f); + NumClientsToTick = FMath::Min(NumClientsToTick, FMath::TruncToInt(ClientUpdatesThisFrame)); + if (NumClientsToTick == 0) { - char pad[0x600]; // idk real size - }; + DeltaTimeOverflow += DeltaSeconds; + return 0; + } + DeltaTimeOverflow = 0.f; + } - FOutBunch CloseBunch{}; - FOutBunch(*ConstructorFOutBunch)(FOutBunch*, UChannel* , bool) = decltype(ConstructorFOutBunch)(__int64(GetModuleHandleW(0)) + 0x194E800); - ConstructorFOutBunch(&CloseBunch, Channel, 1); - // check(!CloseBunch.IsError()); - // check(CloseBunch.bClose); + if (NetCmds::MaxConnectionsToTickPerServerFrame->GetInt() > 0) + { + NumClientsToTick = FMath::Min(ClientConnections.Num(), NetCmds::MaxConnectionsToTickPerServerFrame->GetInt()); + } + */ - // https://imgur.com/a/EtKFkrD + bool bFoundReadyConnection = false; - *(bool*)(__int64(&CloseBunch) + 0xE8) = 1; - *(bool*)(__int64(&CloseBunch) + 0xE6) = 0; + for (int32 ConnIdx = 0; ConnIdx < GetClientConnections().Num(); ConnIdx++) + { + UNetConnection* Connection = GetClientConnections().at(ConnIdx); + // check(Connection); + // check(Connection->State == USOCK_Pending || Connection->State == USOCK_Open || Connection->State == USOCK_Closed); + // checkSlow(Connection->GetUChildConnection() == NULL); - // Serialize DestructInfo - // NET_CHECKSUM(CloseBunch); // This is to mirror the Checksum in UPackageMapClient::SerializeNewActor + AActor* OwningActor = Connection->GetOwningActor(); + if (OwningActor != NULL + // && Connection->State == USOCK_Open + // && (Connection->GetDriver()->Time - Connection->LastReceiveTime < 1.5f) + ) + { + // check(World == OwningActor->GetWorld()); - using UPackageMap = UObject; + bFoundReadyConnection = true; - reinterpret_cast(Connection->GetPackageMap()->VFTable[0x238 / 8])(Connection->GetPackageMap(), &CloseBunch, DestructInfo->ObjOuter.Get(), DestructInfo->NetGUID, DestructInfo->PathName); + AActor* DesiredViewTarget = OwningActor; + if (Connection->GetPlayerController()) + { + if (AActor* ViewTarget = Connection->GetPlayerController()->GetViewTarget()) + { + if (GetWorld() + // ViewTarget->GetWorld() // T(REP) + ) + { + // It is safe to use the player controller's view target. + DesiredViewTarget = ViewTarget; + } + else + { - // UE_LOG(LogNetTraffic, Log, TEXT("SetChannelActorForDestroy: Channel %d. NetGUID <%s> Path: %s. Bits: %d"), ChIndex, *DestructInfo->NetGUID.ToString(), *DestructInfo->PathName, CloseBunch.GetNumBits()); - // UE_LOG(LogNetDormancy, Verbose, TEXT("SetChannelActorForDestroy: Channel %d. NetGUID <%s> Path: %s. Bits: %d"), ChIndex, *DestructInfo->NetGUID.ToString(), *DestructInfo->PathName, CloseBunch.GetNumBits()); + } + } + } + Connection->GetViewTarget() = DesiredViewTarget; - // 0x196E9C0 - reinterpret_cast(Channel->VFTable[0x288 / 8])(Channel, &CloseBunch, false); + for (int32 ChildIdx = 0; ChildIdx < Connection->GetChildren().Num(); ChildIdx++) + { + UNetConnection* Child = Connection->GetChildren().at(ChildIdx); + APlayerController* ChildPlayerController = Child->GetPlayerController(); + if (ChildPlayerController != NULL) + { + Child->GetViewTarget() = ChildPlayerController->GetViewTarget(); + } + else + { + Child->GetViewTarget() = NULL; + } + } + } + else + { + Connection->GetViewTarget() = NULL; + for (int32 ChildIdx = 0; ChildIdx < Connection->GetChildren().Num(); ChildIdx++) + { + Connection->GetChildren().at(ChildIdx)->GetViewTarget() = NULL; + } + } + } + + return bFoundReadyConnection ? NumClientsToTick : 0; +} + +void UNetDriver::ServerReplicateActors_BuildConsiderList(std::vector& OutConsiderList, const float ServerTickTime) +{ + int32 NumInitiallyDormant = 0; + + const bool bUseAdapativeNetFrequency = true; // IsAdaptiveNetUpdateFrequencyEnabled(); // T(REP) + + std::vector ActorsToRemove; + + for (const TSharedPtr& ObjectInfo : GetNetworkObjectList().GetActiveObjects()) + { + FNetworkObjectInfo* ActorInfo = ObjectInfo.Get(); + + if (!ActorInfo->bPendingNetUpdate && World()->GetTimeSeconds() <= ActorInfo->NextUpdateTime) + { + continue; + } + + AActor* Actor = ActorInfo->Actor; + + if (Actor->IsPendingKillPending()) + { + ActorsToRemove.push_back(Actor); + continue; + } + + if (Actor->GetRemoteRole() == ROLE_None) + { + ActorsToRemove.push_back(Actor); + continue; + } + + if (Actor->GetNetDriverName() != this->GetNetDriverName()) // Milxnor: This really shouldn't happen on Fortnite. + { + continue; + } + + if (!Actor->IsActorInitialized()) + { + continue; + } + + ULevel* Level = Actor->GetLevel(); + if (Level->HasVisibilityChangeRequestPending() || Level->IsAssociatingLevel()) + { + continue; + } + + if (Actor->NetDormancy() == ENetDormancy::DORM_Initial && Actor->IsNetStartupActor()) + { + NumInitiallyDormant++; + ActorsToRemove.push_back(Actor); + continue; + } + + // checkSlow(Actor->NeedsLoadForClient()); // We have no business sending this unless the client can load + // checkSlow(World() == Actor->GetWorld()); + + if (ActorInfo->LastNetReplicateTime == 0) + { + ActorInfo->LastNetReplicateTime = World()->GetTimeSeconds(); + ActorInfo->OptimalNetUpdateDelta = 1.0f / Actor->GetNetUpdateFrequency(); + } + + const float ScaleDownStartTime = 2.0f; + const float ScaleDownTimeRange = 5.0f; + + const float LastReplicateDelta = World()->GetTimeSeconds() - ActorInfo->LastNetReplicateTime; + + if (LastReplicateDelta > ScaleDownStartTime) + { + if (Actor->GetMinNetUpdateFrequency() == 0.0f) + { + Actor->GetMinNetUpdateFrequency() = 2.0f; + } + + const float MinOptimalDelta = 1.0f / Actor->GetNetUpdateFrequency(); + const float MaxOptimalDelta = FMath::Max(1.0f / Actor->GetMinNetUpdateFrequency(), MinOptimalDelta); + + const float Alpha = FMath::Clamp((LastReplicateDelta - ScaleDownStartTime) / ScaleDownTimeRange, 0.0f, 1.0f); + ActorInfo->OptimalNetUpdateDelta = FMath::Lerp(MinOptimalDelta, MaxOptimalDelta, Alpha); + } + + if (!ActorInfo->bPendingNetUpdate) + { + const float NextUpdateDelta = bUseAdapativeNetFrequency ? ActorInfo->OptimalNetUpdateDelta : 1.0f / Actor->GetNetUpdateFrequency(); + ActorInfo->NextUpdateTime = World()->GetTimeSeconds() + FMath::SRand() * ServerTickTime + NextUpdateDelta; + ActorInfo->LastNetUpdateTime = GetTime(); + } + + ActorInfo->bPendingNetUpdate = false; + + // ensure(OutConsiderList.Num() < OutConsiderList.Max()); + OutConsiderList.push_back(ActorInfo); + + // Call PreReplication on all actors that will be considered + AActor::originalCallPreReplication(Actor, this); + } + + for (AActor* Actor : ActorsToRemove) + { + GetNetworkObjectList().Remove(Actor); } } -enum ESetChannelActorFlags +int32 UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection* Connection, const std::vector& ConnectionViewers, const std::vector& ConsiderList, const bool bCPUSaturated, std::vector& OutPriorityList, std::vector& OutPriorityActors) { - None = 0, - SkipReplicatorCreation = (1 << 0), - SkipMarkActive = (1 << 1), -}; + GetNetTag()++; + Connection->GetTickCount()++; -TSet* GetClientVisibleLevelNames(UNetConnection* NetConnection) + // Set up to skip all sent temporary actors + for (int32 j = 0; j < Connection->GetSentTemporaries().Num(); j++) + { + Connection->GetSentTemporaries().at(j)->GetNetTag() = GetNetTag(); + } + + // Make list of all actors to consider. + // check(World() == Connection->GetOwningActor()->GetWorld()); + + int32 FinalSortedCount = 0; + int32 DeletedCount = 0; + + // Make weak ptr once for IsActorDormant call + TWeakObjectPtr WeakConnection; // T(REP) + WeakConnection.ObjectIndex = Connection->InternalIndex; + WeakConnection.ObjectSerialNumber = GetItemByIndex(Connection->InternalIndex)->SerialNumber; + + const int32 MaxSortedActors = ConsiderList.size() + GetDestroyedStartupOrDormantActors().Num(); + if (MaxSortedActors > 0) + { + // OutPriorityList = new (FMemStack::Get(), MaxSortedActors) FActorPriority; + // OutPriorityActors = new (FMemStack::Get(), MaxSortedActors) FActorPriority*; + OutPriorityList.reserve(MaxSortedActors); + OutPriorityActors.reserve(MaxSortedActors); + + // check(World() == Connection->GetViewTarget()->GetWorld()); + + // AGameNetworkManager* const NetworkManager = World->NetworkManager; + const bool bLowNetBandwidth = false; // NetworkManager ? NetworkManager->IsInLowBandwidthMode() : false; // Milxnor: Again, this really shouldn't be needed. + + for (FNetworkObjectInfo* ActorInfo : ConsiderList) + { + AActor* Actor = ActorInfo->Actor; + + UActorChannel* Channel = Connection->GetActorChannels().FindRef(ActorInfo->WeakActor); + + if (!Channel) + { + if (!IsLevelInitializedForActor(Actor, Connection)) + { + continue; + } + + if (!IsActorRelevantToConnection(Actor, ConnectionViewers)) + { + continue; + } + } + + UNetConnection* PriorityConnection = Connection; + + if (Actor->IsOnlyRelevantToOwner()) + { + bool bHasNullViewTarget = false; + + PriorityConnection = IsActorOwnedByAndRelevantToConnection(Actor, ConnectionViewers, bHasNullViewTarget); + + if (PriorityConnection == nullptr) + { + if (!bHasNullViewTarget && Channel != NULL && GetTime() - Channel->GetRelevantTime() >= GetRelevantTimeout()) + { + Channel->Close(); + } + + continue; + } + } + else if (// CVarSetNetDormancyEnabled.GetValueOnGameThread() != 0 // Milxnor: This is always enabled no? + true) + { + if (IsActorDormant(ActorInfo, WeakConnection)) + { + continue; + } + + if (ShouldActorGoDormant(Actor, ConnectionViewers, Channel, GetTime(), bLowNetBandwidth)) + { + Channel->StartBecomingDormant(); + } + } + + if (Actor->GetNetTag() != GetNetTag()) + { + Actor->GetNetTag() = GetNetTag(); + + OutPriorityList[FinalSortedCount] = FActorPriority(PriorityConnection, Channel, ActorInfo, ConnectionViewers, bLowNetBandwidth); + // OutPriorityActors[FinalSortedCount] = OutPriorityList + FinalSortedCount; + + FinalSortedCount++; + } + } + + // Add in deleted actors + for (auto It = Connection->GetDestroyedStartupOrDormantActors().CreateIterator(); It; ++It) + { + FActorDestructionInfo& DInfo = GetDestroyedStartupOrDormantActors().FindChecked(*It); + OutPriorityList[FinalSortedCount] = FActorPriority(Connection, &DInfo, ConnectionViewers); + OutPriorityActors[FinalSortedCount] = OutPriorityList.data() + FinalSortedCount; + FinalSortedCount++; + DeletedCount++; + } + + // Sort(OutPriorityActors, FinalSortedCount, FCompareFActorPriority()); + } + + return FinalSortedCount; +} + +int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* Connection, const std::vector& ConnectionViewers, std::vector& PriorityActors, const int32 FinalSortedCount, int32& OutUpdated) { - return (TSet*)(__int64(NetConnection) + 0x336C8); + int32 ActorUpdatesThisConnection = 0; + int32 ActorUpdatesThisConnectionSent = 0; + int32 FinalRelevantCount = 0; + + if (!Connection->IsNetReady(0)) + { + return 0; + } + + for (int32 j = 0; j < FinalSortedCount; j++) + { + FNetworkObjectInfo* ActorInfo = PriorityActors[j]->ActorInfo; + + // Deletion entry + if (ActorInfo == NULL && PriorityActors[j]->DestructionInfo) + { + /* + if (PriorityActors[j]->DestructionInfo->StreamingLevelName != NAME_None && + !Connection->GetClientVisibleLevelNames().Contains(PriorityActors[j]->DestructionInfo->StreamingLevelName) + ) + { + continue; + } + + UActorChannel* Channel = (UActorChannel*)Connection->CreateChannel(CHTYPE_Actor, 1, EChannelCreateFlags::OpenedLocally); + if (Channel) + { + FinalRelevantCount++; + + Channel->SetChannelActorForDestroy(PriorityActors[j]->DestructionInfo); + Connection->GetDestroyedStartupOrDormantActors().Remove(PriorityActors[j]->DestructionInfo->NetGUID); + } + + */ + + continue; + } + + UActorChannel* Channel = PriorityActors[j]->Channel; + if (!Channel || Channel->GetActor()) + { + AActor* Actor = ActorInfo->Actor; + bool bIsRelevant = false; + + const bool bLevelInitializedForActor = IsLevelInitializedForActor(Actor, Connection); + + if (bLevelInitializedForActor) + { + if (!Actor->IsTearOff() && (!Channel || GetTime() - Channel->GetRelevantTime() > 1.f)) + { + if (IsActorRelevantToConnection(Actor, ConnectionViewers)) + { + bIsRelevant = true; + } + } + } + + // if the actor is now relevant or was recently relevant + const bool bIsRecentlyRelevant = bIsRelevant || (Channel && GetTime() - Channel->GetRelevantTime() < GetRelevantTimeout()) || ActorInfo->bForceRelevantNextUpdate; + + ActorInfo->bForceRelevantNextUpdate = false; + + if (bIsRecentlyRelevant) + { + FinalRelevantCount++; + + if (Channel == NULL + // T(REP) + // && GuidCache->SupportsObject(Actor->GetClass()) + // && GuidCache->SupportsObject(Actor->IsNetStartupActor() ? Actor : Actor->GetArchetype()) + ) + { + if (bLevelInitializedForActor) + { + Channel = (UActorChannel*)Connection->CreateChannel(CHTYPE_Actor, 1, EChannelCreateFlags::OpenedLocally); + if (Channel) + { + Channel->SetChannelActor(Actor, ESetChannelActorFlags::None1); + } + } + else if (Actor->GetNetUpdateFrequency() < 1.0f) + { + auto ActorWorld = GetWorld(); // Actor->GetWorld() // T(REP) + ActorInfo->NextUpdateTime = ActorWorld->GetTimeSeconds() + 0.2f * FMath::FRand(); + } + } + + if (Channel) + { + if (bIsRelevant) + { + Channel->GetRelevantTime() = GetTime() + 0.5f * FMath::SRand(); + } + if (Channel->IsNetReady(0)) + { + if (Channel->ReplicateActor()) + { + ActorUpdatesThisConnectionSent++; + + const float MinOptimalDelta = 1.0f / Actor->GetNetUpdateFrequency(); + const float MaxOptimalDelta = FMath::Max(1.0f / Actor->GetMinNetUpdateFrequency(), MinOptimalDelta); + const float DeltaBetweenReplications = (World()->GetTimeSeconds() - ActorInfo->LastNetReplicateTime); + + ActorInfo->OptimalNetUpdateDelta = FMath::Clamp(DeltaBetweenReplications * 0.7f, MinOptimalDelta, MaxOptimalDelta); + ActorInfo->LastNetReplicateTime = World()->GetTimeSeconds(); + } + + ActorUpdatesThisConnection++; + OutUpdated++; + } + else + { + Actor->ForceNetUpdate(); + } + + if (!Connection->IsNetReady(0)) + { + return j; + } + } + } + + if ((!bIsRecentlyRelevant || Actor->IsTearOff()) && Channel != NULL) + { + if (!bLevelInitializedForActor || !Actor->IsNetStartupActor()) + { + Channel->Close(); + } + } + } + } + + return FinalSortedCount; } int32 UNetDriver::ServerReplicateActors() { + float DeltaSeconds = 0; // T(REP) Milxnor: This is a parameter but really I cant be asked to implement it. + + /* + + if ( ClientConnections.Num() == 0 ) + { + return 0; + } + + check( World ); + + */ + int32 Updated = 0; + // Bump the ReplicationFrame value to invalidate any properties marked as "unchanged" for this frame. ++(*(int*)(__int64(this) + Offsets::ReplicationFrame)); - const int32 NumClientsToTick = ServerReplicateActors_PrepConnections(this); + const int32 NumClientsToTick = ServerReplicateActors_PrepConnections(); if (NumClientsToTick == 0) { @@ -591,295 +714,132 @@ int32 UNetDriver::ServerReplicateActors() return 0; } - // AFortWorldSettings* WorldSettings = GetFortWorldSettings(NetDriver->World); + AWorldSettings* WorldSettings = World()->GetWorldSettings(); - // bool bCPUSaturated = false; - float ServerTickTime = GetMaxTickRateHook(); - /* if (ServerTickTime == 0.f) + bool bCPUSaturated = false; + float ServerTickTime = ServerTickRate; // GetEngine()->GetMaxTickRate(DeltaSeconds); // Milxnor: We hook GetMaxTickRate and just return ServerTickRate. + if (ServerTickTime == 0.f) { ServerTickTime = DeltaSeconds; } - else */ + else { ServerTickTime = 1.f / ServerTickTime; - // bCPUSaturated = DeltaSeconds > 1.2f * ServerTickTime; + bCPUSaturated = DeltaSeconds > 1.2f * ServerTickTime; } std::vector ConsiderList; + ConsiderList.reserve(GetNetworkObjectList().GetActiveObjects().Num()); - if (ShouldUseNetworkObjectList()) - ConsiderList.reserve(GetNetworkObjectList().ActiveNetworkObjects.Num()); - - auto World = GetWorld(); - + // Build the consider list (actors that are ready to replicate) ServerReplicateActors_BuildConsiderList(ConsiderList, ServerTickTime); - // LOG_INFO(LogReplication, "Considering {} actors.", ConsiderList.size()); - - static UChannel* (*CreateChannel)(UNetConnection*, int, bool, int32_t) = decltype(CreateChannel)(Addresses::CreateChannel); - static __int64 (*ReplicateActor)(UActorChannel*) = decltype(ReplicateActor)(Addresses::ReplicateActor); - static UObject* (*CreateChannelByName)(UNetConnection * Connection, FName * ChName, EChannelCreateFlags CreateFlags, int32_t ChannelIndex) = decltype(CreateChannelByName)(Addresses::CreateChannel); - static __int64 (*SetChannelActor)(UActorChannel*, AActor*) = decltype(SetChannelActor)(Addresses::SetChannelActor); - static __int64 (*SetChannelActor2)(UActorChannel*, AActor*, ESetChannelActorFlags) = decltype(SetChannelActor2)(Addresses::SetChannelActor); - - for (int32 i = 0; i < this->GetClientConnections().Num(); i++) + for (int32 i = 0; i < GetClientConnections().Num(); ++i) { - UNetConnection* Connection = this->GetClientConnections().at(i); + UNetConnection* Connection = GetClientConnections().at(i); + if (!Connection) continue; - if (!Connection) - continue; + /* + if (CVarNetDormancyValidate.GetValueOnAnyThread() == 2) + { + for (auto It = Connection->DormantReplicatorMap.CreateIterator(); It; ++It) + { + FObjectReplicator& Replicator = It.Value().Get(); + + if (Replicator.OwningChannel != nullptr) + { + Replicator.ValidateAgainstState(Replicator.OwningChannel->GetActor()); + } + } + } + */ if (i >= NumClientsToTick) - continue; - - if (!Connection->GetViewTarget()) - continue; - - if (Addresses::SendClientAdjustment) { + for (int32 ConsiderIdx = 0; ConsiderIdx < ConsiderList.size(); ++ConsiderIdx) + { + AActor* Actor = ConsiderList[ConsiderIdx]->Actor; + + if (Actor != NULL && !ConsiderList[ConsiderIdx]->bPendingNetUpdate) + { + UActorChannel* Channel = Connection->GetActorChannels().FindRef(ConsiderList[ConsiderIdx]->WeakActor); + + if (Channel != NULL && Channel->GetLastUpdateTime() < ConsiderList[ConsiderIdx]->LastNetUpdateTime) + { + ConsiderList[ConsiderIdx]->bPendingNetUpdate = true; + } + } + } + + // Connection->GetTimeSensitive() = false; + } + else if (Connection->GetViewTarget()) + { + // std::vector& ConnectionViewers = WorldSettings->GetReplicationViewers(); // Milxnor: Techinally this is proper but they literally just reset it before rep ends. + std::vector ConnectionViewers; + ConnectionViewers.clear(); + + // new(ConnectionViewers)FNetViewer(Connection, DeltaSeconds); + ConnectionViewers.push_back(FNetViewer(Connection, DeltaSeconds)); + + for (int32 ViewerIndex = 0; ViewerIndex < Connection->GetChildren().Num(); ++ViewerIndex) + { + if (Connection->GetChildren().at(ViewerIndex)->GetViewTarget() != NULL) + { + // new(ConnectionViewers)FNetViewer(Connection->GetChildren().at(ViewerIndex), DeltaSeconds); + ConnectionViewers.push_back(FNetViewer(Connection->GetChildren().at(ViewerIndex), DeltaSeconds)); + } + } + if (Connection->GetPlayerController()) { - static void (*SendClientAdjustment)(APlayerController*) = decltype(SendClientAdjustment)(Addresses::SendClientAdjustment); - SendClientAdjustment(Connection->GetPlayerController()); + APlayerController::originalSendClientAdjustment(Connection->GetPlayerController()); } - } - // Make weak ptr once for IsActorDormant call - TWeakObjectPtr WeakConnection{}; - WeakConnection.ObjectIndex = Connection->InternalIndex; - WeakConnection.ObjectSerialNumber = GetItemByIndex(Connection->InternalIndex)->SerialNumber; - - /* GetNetTag()++; - Connection->GetTickCount()++; - - for (int32 j = 0; j < Connection->GetSentTemporaries().Num(); j++) // Set up to skip all sent temporary actors - { - Connection->GetSentTemporaries().at(j)->GetNetTag() = GetNetTag(); - } */ - - std::vector DeletionEntries; - -#if 0 - auto ConnectionDestroyedStartupOrDormantActors = GetDestroyedStartupOrDormantActors(Connection); - - if (ConnectionDestroyedStartupOrDormantActors) - { - auto DriverDestroyedStartupOrDormantActors = GetDestroyedStartupOrDormantActors(this); - - if (DriverDestroyedStartupOrDormantActors) + for (int32 ChildIdx = 0; ChildIdx < Connection->GetChildren().Num(); ChildIdx++) { - for (FNetworkGUID& ConnectionIt : *ConnectionDestroyedStartupOrDormantActors) + if (Connection->GetChildren().at(ChildIdx)->GetPlayerController() != NULL) { - FActorDestructionInfo* DInfo = nullptr; - - for (TPair& DriverIt : *DriverDestroyedStartupOrDormantActors) - { - if (DriverIt.First == ConnectionIt) - { - DInfo = &DriverIt.Second; - break; - } - } - - if (!DInfo) continue; // should never happen - - DeletionEntries.push_back(DInfo); + APlayerController::originalSendClientAdjustment(Connection->GetChildren().at(ChildIdx)->GetPlayerController()); } } - } - LOG_INFO(LogDev, "DeletionEntries: {}", DeletionEntries.size()); -#endif + std::vector PriorityList; + std::vector PriorityActors; - for (FActorDestructionInfo* DeletionEntry : DeletionEntries) - { - LOG_INFO(LogDev, "AA: {}", DeletionEntry->PathName.Data.Data ? DeletionEntry->PathName.ToString() : "Null"); + const int32 FinalSortedCount = ServerReplicateActors_PrioritizeActors(Connection, ConnectionViewers, ConsiderList, bCPUSaturated, PriorityList, PriorityActors); - if (DeletionEntry->StreamingLevelName != -1) + // Process the sorted list of actors for this connection + const int32 LastProcessedActor = ServerReplicateActors_ProcessPrioritizedActors(Connection, ConnectionViewers, PriorityActors, FinalSortedCount, Updated); + + for (int32 k = LastProcessedActor; k < FinalSortedCount; ++k) { - auto ClientVisibleLevelNames = GetClientVisibleLevelNames(Connection); - - bool bFound = false; - - for (FName& ClientVisibleLevelName : *ClientVisibleLevelNames) + if (!PriorityActors[k]->ActorInfo) { - if (ClientVisibleLevelName == DeletionEntry->StreamingLevelName) - { - bFound = true; - break; - } - } - - if (!bFound) - continue; - } - - UActorChannel* Channel = nullptr; - - if (Engine_Version >= 422) - { - FString ActorStr = L"Actor"; - FName ActorName = UKismetStringLibrary::Conv_StringToName(ActorStr); - - int ChannelIndex = -1; // 4294967295 - Channel = (UActorChannel*)CreateChannelByName(Connection, &ActorName, EChannelCreateFlags::OpenedLocally, ChannelIndex); - } - else - { - Channel = (UActorChannel*)CreateChannel(Connection, 2, true, -1); - } - - if (Channel) - { - // FinalRelevantCount++; - - SetChannelActorForDestroy(Channel, DeletionEntry); // Send a close bunch on the new channel - GetDestroyedStartupOrDormantActors(Connection)->Remove(DeletionEntry->NetGUID); // Remove from connections to-be-destroyed list (close bunch of reliable, so it will make it there) - } - } - - for (auto& ActorInfo : ConsiderList) - { - if (!ActorInfo || !ActorInfo->Actor) - continue; - - auto Actor = ActorInfo->Actor; - - auto Channel = FindChannel(Actor, Connection); - - /* if (IsActorDormant(ActorInfo, WeakConnection)) - { - continue; - } */ - - std::vector ConnectionViewers; - ConnectionViewers.push_back(ConstructNetViewer(Connection)); - - const bool bLevelInitializedForActor = IsLevelInitializedForActor(Actor, Connection); - - if (!Channel) - { - // if (!IsLevelInitializedForActor(Actor, Connection)) - if (!bLevelInitializedForActor) - { - // If the level this actor belongs to isn't loaded on client, don't bother sending continue; } - /* if (!IsActorRelevantToConnection(Actor, ConnectionViewers)) + AActor* Actor = PriorityActors[k]->ActorInfo->Actor; + + UActorChannel* Channel = PriorityActors[k]->Channel; + + if (Channel != NULL && GetTime() - Channel->GetRelevantTime() <= 1.f) { - // If not relevant (and we don't have a channel), skip - continue; - } */ - } - - bool bLowNetBandwidth = false; - - // See of actor wants to try and go dormant - /* if (ShouldActorGoDormant(Actor, ConnectionViewers, Channel, GetTime(), bLowNetBandwidth)) - { - // LOG_INFO(LogDev, "Actor is going dormant!"); - - // Channel is marked to go dormant now once all properties have been replicated (but is not dormant yet) - Channel->StartBecomingDormant(); - } */ - - if (Addresses::ActorChannelClose && Offsets::IsNetRelevantFor) - { - static void (*ActorChannelClose)(UActorChannel*) = decltype(ActorChannelClose)(Addresses::ActorChannelClose); - - if (!Actor->IsAlwaysRelevant() && !Actor->UsesOwnerRelevancy() && !Actor->IsOnlyRelevantToOwner()) + PriorityActors[k]->ActorInfo->bPendingNetUpdate = true; + } + else if (IsActorRelevantToConnection(Actor, ConnectionViewers)) { - if (Connection && Connection->GetViewTarget()) + PriorityActors[k]->ActorInfo->bPendingNetUpdate = true; + if (Channel != NULL) { - auto Viewer = Connection->GetViewTarget(); - auto Loc = Viewer->GetActorLocation(); - - if (!IsActorRelevantToConnection(Actor, ConnectionViewers)) - { - // LOG_INFO(LogReplication, "Actor is not relevant!"); - - if (Channel) - ActorChannelClose(Channel); - - continue; - } + Channel->GetRelevantTime() = GetTime() + 0.5f * FMath::SRand(); } } } - - if (!Channel) - { - if (Actor->IsA(APlayerController::StaticClass()) && Actor != Connection->GetPlayerController()) // isnetrelevantfor should handle this iirc - continue; - - if (bLevelInitializedForActor) - { - if (Engine_Version >= 422) - { - FString ActorStr = L"Actor"; - FName ActorName = UKismetStringLibrary::Conv_StringToName(ActorStr); - - int ChannelIndex = -1; // 4294967295 - Channel = (UActorChannel*)CreateChannelByName(Connection, &ActorName, EChannelCreateFlags::OpenedLocally, ChannelIndex); - } - else - { - Channel = (UActorChannel*)CreateChannel(Connection, 2, true, -1); - } - - if (Channel) - { - if (Engine_Version >= 500) - SetChannelActor(Channel, Actor); - else - SetChannelActor2(Channel, Actor, ESetChannelActorFlags::None); - } - } - - else if (Actor->GetNetUpdateFrequency() < 1.0f) - { - ActorInfo->NextUpdateTime = UGameplayStatics::GetTimeSeconds(GetWorld()) + 0.2f * FRand(); - } - } - - if (Channel) - { - if (ReplicateActor(Channel)) - { - if (ShouldUseNetworkObjectList()) - { - // LOG_INFO(LogReplication, "Replicated Actor!"); - auto TimeSeconds = UGameplayStatics::GetTimeSeconds(World); - const float MinOptimalDelta = 1.0f / Actor->GetNetUpdateFrequency(); - const float MaxOptimalDelta = max(1.0f / Actor->GetMinNetUpdateFrequency(), MinOptimalDelta); - const float DeltaBetweenReplications = (TimeSeconds - ActorInfo->LastNetReplicateTime); - - // Choose an optimal time, we choose 70% of the actual rate to allow frequency to go up if needed - ActorInfo->OptimalNetUpdateDelta = std::clamp(DeltaBetweenReplications * 0.7f, MinOptimalDelta, MaxOptimalDelta); // should we use fmath? - ActorInfo->LastNetReplicateTime = TimeSeconds; - } - } - } + ConnectionViewers.clear(); } } - // shuffle the list of connections if not all connections were ticked - /* - if (NumClientsToTick < NetDriver->ClientConnections.Num()) - { - int32 NumConnectionsToMove = NumClientsToTick; - while (NumConnectionsToMove > 0) - { - // move all the ticked connections to the end of the list so that the other connections are considered first for the next frame - UNetConnection* Connection = NetDriver->ClientConnections[0]; - NetDriver->ClientConnections.RemoveAt(0, 1); - NetDriver->ClientConnections.Add(Connection); - NumConnectionsToMove--; - } - } - */ - return Updated; } \ No newline at end of file diff --git a/Project Reboot 3.0/NetDriver.h b/Project Reboot 3.0/NetDriver.h index a92e4d3..9afe82d 100644 --- a/Project Reboot 3.0/NetDriver.h +++ b/Project Reboot 3.0/NetDriver.h @@ -56,15 +56,6 @@ struct FNetworkObjectInfo TSet> RecentlyDormantConnections; }; -struct FNetViewer -{ - UNetConnection* Connection; // 0x0000(0x0008) (ZeroConstructor, IsPlainOldData) - AActor* InViewer; // 0x0008(0x0008) (ZeroConstructor, IsPlainOldData) - AActor* ViewTarget; // 0x0010(0x0008) (ZeroConstructor, IsPlainOldData) - FVector ViewLocation; // 0x0018(0x000C) (IsPlainOldData) - FVector ViewDir; -}; - class FNetworkObjectList { public: @@ -77,6 +68,7 @@ public: TMap, int32> NumDormantObjectsPerConnection; void Remove(AActor* const Actor); + const FNetworkObjectSet& GetActiveObjects() const { return ActiveNetworkObjects; } }; struct FActorPriority @@ -130,6 +122,13 @@ public: static void TickFlushHook(UNetDriver* NetDriver); + TMap& GetDestroyedStartupOrDormantActors() // T(REP) + { + static int off = Fortnite_Version == 1.11 ? 0x228 : 0; // 0x240 + + return *(TMap*)(__int64(this) + off); + } + int& GetMaxInternetClientRate() { static auto MaxInternetClientRateOffset = GetOffset("MaxInternetClientRate"); @@ -142,13 +141,19 @@ public: return Get(MaxClientRateOffset); } + FName& GetNetDriverName() + { + static auto NetDriverNameOffset = GetOffset("NetDriverName"); + return Get(NetDriverNameOffset); + } + FNetGUIDCache* GetGuidCache() { static auto GuidCacheOffset = GetOffset("WorldPackage") + 8; // checked for 1.11 return GetPtr(GuidCacheOffset); } - UWorld*& GetNetDriverWorld() const + UWorld*& World() const { static auto WorldOffset = GetOffset("World"); return Get(WorldOffset); @@ -195,8 +200,9 @@ public: bool InitListen(FNetworkNotify* InNotify, FURL& ListenURL, bool bReuseAddressAndPort, FString& Error) { return InitListenOriginal(this, InNotify, ListenURL, bReuseAddressAndPort, Error); } void SetWorld(UWorld* World) { return SetWorldOriginal(this, World); } int32 ServerReplicateActors(); - int32 ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* Connection, const std::vector& ConnectionViewers, FActorPriority** PriorityActors, const int32 FinalSortedCount, int32& OutUpdated); + int32 ServerReplicateActors_PrepConnections(); + int32 ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* Connection, const std::vector& ConnectionViewers, std::vector& PriorityActors, const int32 FinalSortedCount, int32& OutUpdated); void ServerReplicateActors_BuildConsiderList(std::vector& OutConsiderList, const float ServerTickTime); - int32 ServerReplicateActors_PrioritizeActors(UNetConnection* Connection, const std::vector& ConnectionViewers, const std::vector ConsiderList, const bool bCPUSaturated, FActorPriority*& OutPriorityList, FActorPriority**& OutPriorityActors); + int32 ServerReplicateActors_PrioritizeActors(UNetConnection* Connection, const std::vector& ConnectionViewers, const std::vector& ConsiderList, const bool bCPUSaturated, std::vector& OutPriorityList, std::vector& OutPriorityActors); FNetworkObjectList& GetNetworkObjectList(); }; \ No newline at end of file diff --git a/Project Reboot 3.0/NetworkObjectList.cpp b/Project Reboot 3.0/NetworkObjectList.cpp index 92e4b39..e2d44c7 100644 --- a/Project Reboot 3.0/NetworkObjectList.cpp +++ b/Project Reboot 3.0/NetworkObjectList.cpp @@ -1,7 +1,8 @@ #include "NetDriver.h" -void FNetworkObjectList::Remove(AActor* const Actor) +void FNetworkObjectList::Remove(AActor* const Actor) // T(REP) { +#if 0 if (Actor == nullptr) { return; @@ -103,5 +104,7 @@ void FNetworkObjectList::Remove(AActor* const Actor) } } +#endif + // check((ActiveNetworkObjects.Num() + ObjectsDormantOnAllConnections.Num()) == AllNetworkObjects.Num()); } \ No newline at end of file diff --git a/Project Reboot 3.0/NetworkingDistanceConstants.h b/Project Reboot 3.0/NetworkingDistanceConstants.h new file mode 100644 index 0000000..5836480 --- /dev/null +++ b/Project Reboot 3.0/NetworkingDistanceConstants.h @@ -0,0 +1,11 @@ +#pragma once + +// magic number distances used by AI/networking +#define CLOSEPROXIMITY 500.f +#define NEARSIGHTTHRESHOLD 2000.f +#define MEDSIGHTTHRESHOLD 3162.f +#define FARSIGHTTHRESHOLD 8000.f +#define CLOSEPROXIMITYSQUARED (CLOSEPROXIMITY*CLOSEPROXIMITY) +#define NEARSIGHTTHRESHOLDSQUARED (NEARSIGHTTHRESHOLD*NEARSIGHTTHRESHOLD) +#define MEDSIGHTTHRESHOLDSQUARED (MEDSIGHTTHRESHOLD*MEDSIGHTTHRESHOLD) +#define FARSIGHTTHRESHOLDSQUARED (FARSIGHTTHRESHOLD*FARSIGHTTHRESHOLD) \ No newline at end of file diff --git a/Project Reboot 3.0/Object.cpp b/Project Reboot 3.0/Object.cpp index 81e149b..8674f6f 100644 --- a/Project Reboot 3.0/Object.cpp +++ b/Project Reboot 3.0/Object.cpp @@ -7,6 +7,21 @@ #include "UObjectArray.h" #include "Package.h" +UObject* UObject::GetTypedOuter(UClass* Target) const +{ + // ensureMsgf(Target != UPackage::StaticClass(), TEXT("Calling GetTypedOuter to retrieve a package is now invalid, you should use GetPackage() instead.")); + + UObject* Result = NULL; + for (UObject* NextOuter = GetOuter(); Result == NULL && NextOuter != NULL; NextOuter = NextOuter->GetOuter()) + { + if (NextOuter->IsA(Target)) + { + Result = NextOuter; + } + } + return Result; +} + void* UObject::GetProperty(const std::string& ChildName, bool bWarnIfNotFound) const { for (auto CurrentClass = ClassPrivate; CurrentClass; CurrentClass = *(UClass**)(__int64(CurrentClass) + Offsets::SuperStruct)) diff --git a/Project Reboot 3.0/Object.h b/Project Reboot 3.0/Object.h index d03ee4d..5609110 100644 --- a/Project Reboot 3.0/Object.h +++ b/Project Reboot 3.0/Object.h @@ -78,6 +78,14 @@ public: void SetBitfieldValue(int Offset, uint8_t FieldMask, bool NewValue); void SetBitfieldValue(const std::string& ChildName, uint8_t FieldMask, bool NewValue) { return SetBitfieldValue(GetOffset(ChildName), FieldMask, NewValue); } + UObject* GetTypedOuter(UClass* Target) const; + + template + T* GetTypedOuter() const + { + return (T*)GetTypedOuter(T::StaticClass()); + } + /* template T& GetCached(const std::string& ChildName) { diff --git a/Project Reboot 3.0/PlayerController.h b/Project Reboot 3.0/PlayerController.h index 76c14d7..78d16c8 100644 --- a/Project Reboot 3.0/PlayerController.h +++ b/Project Reboot 3.0/PlayerController.h @@ -10,6 +10,8 @@ class APlayerController : public AController { public: + static inline void (*originalSendClientAdjustment)(APlayerController*); + UCheatManager*& GetCheatManager() { static auto CheatManagerOffset = this->GetOffset("CheatManager"); diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj b/Project Reboot 3.0/Project Reboot 3.0.vcxproj index d13ab8f..c63bb84 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj @@ -308,6 +308,8 @@ + + @@ -446,6 +448,7 @@ + @@ -499,6 +502,7 @@ + diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters index f97598b..7ab69b4 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters @@ -987,6 +987,18 @@ Reboot\Public + + Engine\Source\Runtime\Engine\Classes\Engine + + + Engine\Source\Runtime\Engine\Classes\GameFramework + + + Engine\Source\Runtime\Core\Public\Templates + + + Engine\Source\Runtime\Engine\Public + diff --git a/Project Reboot 3.0/Set.h b/Project Reboot 3.0/Set.h index f400dd8..4616693 100644 --- a/Project Reboot 3.0/Set.h +++ b/Project Reboot 3.0/Set.h @@ -1,6 +1,10 @@ +// Copied from 4.19 + #pragma once #include "SparseArray.h" +#include "ChooseClass.h" +#include "UnrealTypeTraits.h" template class TSetElement @@ -32,112 +36,434 @@ public: } }; -template +/** Either NULL or an identifier for an element of a set. */ +class FSetElementId +{ +public: + + template + friend class TSet; + + friend class FScriptSet; + + /** Default constructor. */ + FORCEINLINE FSetElementId() : + Index(INDEX_NONE) + {} + + /** @return a boolean value representing whether the id is NULL. */ + FORCEINLINE bool IsValidId() const + { + return Index != INDEX_NONE; + } + + /** Comparison operator. */ + FORCEINLINE friend bool operator==(const FSetElementId& A, const FSetElementId& B) + { + return A.Index == B.Index; + } + + FORCEINLINE int32 AsInteger() const + { + return Index; + } + + FORCEINLINE static FSetElementId FromInteger(int32 Integer) + { + return FSetElementId(Integer); + } + +private: + + /** The index of the element in the set's element array. */ + int32 Index; + + /** Initialization constructor. */ + FORCEINLINE FSetElementId(int32 InIndex) : + Index(InIndex) + {} + + /** Implicit conversion to the element index. */ + FORCEINLINE operator int32() const + { + return Index; + } +}; + +template +struct BaseKeyFuncs +{ + typedef InKeyType KeyType; + typedef typename TCallTraits::ParamType KeyInitType; + typedef typename TCallTraits::ParamType ElementInitType; + + enum { bAllowDuplicateKeys = bInAllowDuplicateKeys }; +}; + +/** + * A default implementation of the KeyFuncs used by TSet which uses the element as a key. + */ +template +struct DefaultKeyFuncs : BaseKeyFuncs +{ + typedef typename TCallTraits::ParamType KeyInitType; + typedef typename TCallTraits::ParamType ElementInitType; + + /** + * @return The key used to index the given element. + */ + static FORCEINLINE KeyInitType GetSetKey(ElementInitType Element) + { + return Element; + } + + /** + * @return True if the keys match. + */ + static FORCEINLINE bool Matches(KeyInitType A, KeyInitType B) + { + return A == B; + } + + /** Calculates a hash index for a key. */ + static FORCEINLINE uint32 GetKeyHash(KeyInitType Key) + { + return GetTypeHash(Key); + } +}; + +template< + typename InElementType, + typename KeyFuncs = DefaultKeyFuncs, + typename Allocator = FDefaultSetAllocator +> +class TSet; + +template< + typename InElementType, + typename KeyFuncs /*= DefaultKeyFuncs*/, + typename Allocator /*= FDefaultSetAllocator*/ +> class TSet { private: friend TSparseArray; public: - typedef TSetElement ElementType; - typedef TSparseArrayElementOrListLink ArrayElementType; + typedef typename KeyFuncs::KeyInitType KeyInitType; + typedef typename KeyFuncs::ElementInitType ElementInitType; + + typedef TSetElement SetElementType; + + typedef InElementType ElementType; +public: + typedef TSparseArray ElementArrayType; + typedef typename Allocator::HashAllocator::template ForElementType HashType; + + ElementArrayType Elements; + + mutable HashType Hash; + mutable int32 HashSize; + + FORCEINLINE FSetElementId& GetTypedHash(int32 HashIndex) const + { + return ((FSetElementId*)Hash.GetAllocation())[HashIndex & (HashSize - 1)]; + } public: - TSparseArray Elements; + FORCEINLINE ElementType* Find(KeyInitType Key) + { + FSetElementId ElementId = FindId(Key); + if (ElementId.IsValidId()) + { + return &Elements[ElementId].Value; + } + else + { + return NULL; + } + } - mutable TInlineAllocator<1>::ForElementType Hash; - mutable int32 HashSize; + /** + * Finds an element with the given key in the set. + * @param Key - The key to search for. + * @return A const pointer to an element with the given key. If no element in the set has the given key, this will return NULL. + */ + FORCEINLINE const ElementType* Find(KeyInitType Key) const + { + FSetElementId ElementId = FindId(Key); + if (ElementId.IsValidId()) + { + return &Elements[ElementId].Value; + } + else + { + return NULL; + } + } -public: - class FBaseIterator + + /** Adds an element to the hash. */ + FORCEINLINE void HashElement(FSetElementId ElementId, const SetElementType& Element) const + { + // Compute the hash bucket the element goes in. + Element.HashIndex = KeyFuncs::GetKeyHash(KeyFuncs::GetSetKey(Element.Value)) & (HashSize - 1); + + // Link the element into the hash bucket. + Element.HashNextId = GetTypedHash(Element.HashIndex); + GetTypedHash(Element.HashIndex) = ElementId; + } + + /** + * Checks if the hash has an appropriate number of buckets, and if not resizes it. + * @param NumHashedElements - The number of elements to size the hash for. + * @param bAllowShrinking - true if the hash is allowed to shrink. + * @return true if the set was rehashed. + */ + bool ConditionalRehash(int32 NumHashedElements, bool bAllowShrinking = false) const + { + // Calculate the desired hash size for the specified number of elements. + const int32 DesiredHashSize = Allocator::GetNumberOfHashBuckets(NumHashedElements); + + // If the hash hasn't been created yet, or is smaller than the desired hash size, rehash. + if (NumHashedElements > 0 && + (!HashSize || + HashSize < DesiredHashSize || + (HashSize > DesiredHashSize && bAllowShrinking))) + { + HashSize = DesiredHashSize; + Rehash(); + return true; + } + else + { + return false; + } + } + + /** Resizes the hash. */ + void Rehash() const + { + // Free the old hash. + Hash.ResizeAllocation(0, 0, sizeof(FSetElementId)); + + int32 LocalHashSize = HashSize; + if (LocalHashSize) + { + // Allocate the new hash. + // checkSlow(FMath::IsPowerOfTwo(HashSize)); + Hash.ResizeAllocation(0, LocalHashSize, sizeof(FSetElementId)); + for (int32 HashIndex = 0; HashIndex < LocalHashSize; ++HashIndex) + { + GetTypedHash(HashIndex) = FSetElementId(); + } + + // Add the existing elements to the new hash. + for (typename ElementArrayType::TConstIterator ElementIt(Elements); ElementIt; ++ElementIt) + { + HashElement(FSetElementId(ElementIt.GetIndex()), *ElementIt); + } + } + } + + template + class TBaseIterator { private: - TSet& IteratedSet; - TSparseArray::FBaseIterator ElementIt; + friend class TSet; + + typedef typename TChooseClass::Result ItElementType; public: - FORCEINLINE FBaseIterator(const TSet& InSet, TSparseArray>::FBaseIterator InElementIt) - : IteratedSet(const_cast&>(InSet)) - , ElementIt(InElementIt) + typedef typename TChooseClass< + bConst, + typename TChooseClass::Result, + typename TChooseClass::Result + >::Result ElementItType; + + FORCEINLINE TBaseIterator(const ElementItType& InElementIt) + : ElementIt(InElementIt) { } - FORCEINLINE explicit operator bool() const - { - return (bool)ElementIt; - } - FORCEINLINE TSet::FBaseIterator& operator++() + /** Advances the iterator to the next element. */ + FORCEINLINE TBaseIterator& operator++() { ++ElementIt; return *this; } - FORCEINLINE bool operator==(const TSet::FBaseIterator& OtherIt) const + + /** conversion to "bool" returning true if the iterator is valid. */ + FORCEINLINE explicit operator bool() const { - return ElementIt == OtherIt.ElementIt; + return !!ElementIt; } - FORCEINLINE bool operator!=(const TSet::FBaseIterator& OtherIt) const + /** inverse of the "bool" operator */ + FORCEINLINE bool operator !() const { - return ElementIt != OtherIt.ElementIt; + return !(bool)*this; } - FORCEINLINE TSet::FBaseIterator& operator=(TSet::FBaseIterator& OtherIt) + + // Accessors. + FORCEINLINE FSetElementId GetId() const { - return ElementIt = OtherIt.ElementIt; + return TSet::IndexToId(ElementIt.GetIndex()); } - FORCEINLINE SetType& operator*() + FORCEINLINE ItElementType* operator->() const { - return (*ElementIt).Value; + return &ElementIt->Value; } - FORCEINLINE const SetType& operator*() const + FORCEINLINE ItElementType& operator*() const { - return &((*ElementIt).Value); + return ElementIt->Value; } - FORCEINLINE SetType* operator->() + + FORCEINLINE friend bool operator==(const TBaseIterator& Lhs, const TBaseIterator& Rhs) { return Lhs.ElementIt == Rhs.ElementIt; } + FORCEINLINE friend bool operator!=(const TBaseIterator& Lhs, const TBaseIterator& Rhs) { return Lhs.ElementIt != Rhs.ElementIt; } + + ElementItType ElementIt; + }; + + /** The base type of whole set iterators. */ + template + class TBaseKeyIterator + { + private: + typedef typename TChooseClass::Result SetType; + typedef typename TChooseClass::Result ItElementType; + + public: + /** Initialization constructor. */ + FORCEINLINE TBaseKeyIterator(SetType& InSet, KeyInitType InKey) + : Set(InSet) + , Key(InKey) + , Id() { - return &((*ElementIt).Value); + // The set's hash needs to be initialized to find the elements with the specified key. + Set.ConditionalRehash(Set.Elements.Num()); + if (Set.HashSize) + { + NextId = Set.GetTypedHash(KeyFuncs::GetKeyHash(Key)); + ++(*this); + } + /* // Milxnor: This is only on newer builds? + else + { + NextIndex = INDEX_NONE; + } + */ } - FORCEINLINE const SetType* operator->() const + + /** Advances the iterator to the next element. */ + FORCEINLINE TBaseKeyIterator& operator++() { - return &(*ElementIt).Value; + Id = NextId; + + while (Id.IsValidId()) + { + NextId = Set.GetInternalElement(Id).HashNextId; + // checkSlow(Id != NextId); + + if (KeyFuncs::Matches(KeyFuncs::GetSetKey(Set[Id]), Key)) + { + break; + } + + Id = NextId; + } + return *this; } - FORCEINLINE const int32 GetIndex() const + + /** conversion to "bool" returning true if the iterator is valid. */ + FORCEINLINE explicit operator bool() const { - return ElementIt.GetIndex(); + return Id.IsValidId(); } - FORCEINLINE ElementType& GetSetElement() + /** inverse of the "bool" operator */ + FORCEINLINE bool operator !() const { - return *ElementIt; + return !(bool)*this; } - FORCEINLINE const ElementType& GetSetElement() const + + // Accessors. + FORCEINLINE ItElementType* operator->() const { - return *ElementIt; + return &Set[Id]; } - FORCEINLINE bool IsElementValid() const + FORCEINLINE ItElementType& operator*() const + { + return Set[Id]; + } + + protected: + SetType& Set; + typename TTypeTraits::ConstPointerType Key; + FSetElementId Id; + FSetElementId NextId; + }; + + FORCEINLINE const SetElementType& GetInternalElement(FSetElementId Id) const + { + return Elements[Id]; + } + FORCEINLINE SetElementType& GetInternalElement(FSetElementId Id) + { + return Elements[Id]; + } + +public: + class TConstIterator : public TBaseIterator + { + friend class TSet; + + public: + FORCEINLINE TConstIterator(const TSet& InSet) + : TBaseIterator(begin(InSet.Elements)) { - return ElementIt.IsElementValid(); } }; -public: - FORCEINLINE TSet::FBaseIterator begin() + class TIterator : public TBaseIterator { - return TSet::FBaseIterator(*this, Elements.begin()); - } - FORCEINLINE const TSet::FBaseIterator begin() const + friend class TSet; + + public: + FORCEINLINE TIterator(TSet& InSet) + : TBaseIterator(begin(InSet.Elements)) + , Set(InSet) + { + } + + /** Removes the current element from the set. */ + /* // T(R) + FORCEINLINE void RemoveCurrent() + { + Set.Remove(TBaseIterator::GetId()); + } + */ + + private: + TSet& Set; + }; + + using TRangedForConstIterator = TBaseIterator; + using TRangedForIterator = TBaseIterator; + + static FORCEINLINE FSetElementId IndexToId(int32 Index) { - return TSet::FBaseIterator(*this, Elements.begin()); - } - FORCEINLINE TSet::FBaseIterator end() - { - return TSet::FBaseIterator(*this, Elements.end()); - } - FORCEINLINE const TSet::FBaseIterator end() const - { - return TSet::FBaseIterator(*this, Elements.end()); + return FSetElementId(Index); } - FORCEINLINE SetType& operator[](int Index) + /** Creates an iterator for the contents of this set */ + FORCEINLINE TIterator CreateIterator() { - return Elements[Index].ElementData.Value; + return TIterator(*this); + } + + /** Creates a const iterator for the contents of this set */ + FORCEINLINE TConstIterator CreateConstIterator() const + { + return TConstIterator(*this); } FORCEINLINE int32 Num() const @@ -156,65 +482,39 @@ public: { return Elements; } - FORCEINLINE const TBitArray& GetAllocationFlags() const - { - return Elements.GetAllocationFlags(); - } FORCEINLINE bool IsIndexValid(int32 IndexToCheck) const { return Elements.IsIndexValid(IndexToCheck); } - FORCEINLINE const bool Contains(const SetType& ElementToLookFor) const + FSetElementId FindId(KeyInitType Key) const { - if (Num() <= 0) - return false; - - for (SetType Element : *this) + if (Elements.Num()) { - if (Element == ElementToLookFor) - return true; - } - return false; - } - FORCEINLINE const int32 Find(const SetType& ElementToLookFor) const - { - for (auto It = this->begin(); It != this->end(); ++It) - { - if (*It == ElementToLookFor) + for (FSetElementId ElementId = GetTypedHash(KeyFuncs::GetKeyHash(Key)); + ElementId.IsValidId(); + ElementId = Elements[ElementId].HashNextId) { - return It.GetIndex(); + if (KeyFuncs::Matches(KeyFuncs::GetSetKey(Elements[ElementId].Value), Key)) + { + return ElementId; + } } } - - return -1; + return FSetElementId(); } - FORCEINLINE bool Remove(const SetType& ElementToRemove) + + FORCEINLINE bool Contains(KeyInitType Key) const { - auto Idx = Find(ElementToRemove); - - if (Idx == -1) - return false; - - Elements.RemoveAt(Idx); - return true; - } - FORCEINLINE bool Remove(int Index) - { - Elements.RemoveAt(Index); - return true; + return FindId(Key).IsValidId(); } + +private: + /** + * DO NOT USE DIRECTLY + * STL-like iterators to enable range-based for loop support. + */ + FORCEINLINE friend TRangedForIterator begin(TSet& Set) { return TRangedForIterator(begin(Set.Elements)); } + FORCEINLINE friend TRangedForConstIterator begin(const TSet& Set) { return TRangedForConstIterator(begin(Set.Elements)); } + FORCEINLINE friend TRangedForIterator end(TSet& Set) { return TRangedForIterator(end(Set.Elements)); } + FORCEINLINE friend TRangedForConstIterator end(const TSet& Set) { return TRangedForConstIterator(end(Set.Elements)); } }; - - -/* template //, typename KeyFuncs, typename Allocator> -class TSet -{ -public: - typedef TSetElement ElementType; - typedef TSparseArrayElementOrListLink ArrayElementType; - - TSparseArray Elements; - - mutable TInlineAllocator<1>::ForElementType Hash; - mutable int32 HashSize; -}; */ \ No newline at end of file diff --git a/Project Reboot 3.0/SparseArray.h b/Project Reboot 3.0/SparseArray.h index 4698b75..c3ead16 100644 --- a/Project Reboot 3.0/SparseArray.h +++ b/Project Reboot 3.0/SparseArray.h @@ -2,259 +2,229 @@ #include "Array.h" #include "BitArray.h" +#include "ChooseClass.h" #include "log.h" +#include "TypeCompatibleBytes.h" #define INDEX_NONE -1 -template -union TSparseArrayElementOrListLink +template +union TSparseArrayElementOrFreeListLink { - TSparseArrayElementOrListLink(ElementType& InElement) - : ElementData(InElement) - { - } - TSparseArrayElementOrListLink(ElementType&& InElement) - : ElementData(InElement) - { - } - - TSparseArrayElementOrListLink(int32 InPrevFree, int32 InNextFree) - : PrevFreeIndex(InPrevFree) - , NextFreeIndex(InNextFree) - { - } - - TSparseArrayElementOrListLink operator=(const TSparseArrayElementOrListLink& Other) - { - return TSparseArrayElementOrListLink(Other.NextFreeIndex, Other.PrevFreeIndex); - } - /** If the element is allocated, its value is stored here. */ ElementType ElementData; struct { /** If the element isn't allocated, this is a link to the previous element in the array's free list. */ - int PrevFreeIndex; + int32 PrevFreeIndex; /** If the element isn't allocated, this is a link to the next element in the array's free list. */ - int NextFreeIndex; + int32 NextFreeIndex; }; }; -template +template +class TSparseArray; + +template class TSparseArray { -public: - typedef TSparseArrayElementOrListLink FSparseArrayElement; + using ElementType = InElementType; - TArray Data; - TBitArray AllocationFlags; +public: + typedef TSparseArrayElementOrFreeListLink< + TAlignedBytes + > FElementOrFreeListLink; + + typedef TArray DataType; + DataType Data; + + typedef TBitArray AllocationBitArrayType; + AllocationBitArrayType AllocationFlags; + + /** The index of an unallocated element in the array that currently contains the head of the linked list of free elements. */ int32 FirstFreeIndex; + + /** The number of elements in the free list. */ int32 NumFreeIndices; FORCEINLINE int32 Num() const { return Data.Num() - NumFreeIndices; } - - class FBaseIterator + template + class TBaseIterator { + public: + typedef TConstSetBitIterator BitArrayItType; + private: - TSparseArray& IteratedArray; - TBitArray::FSetBitIterator BitArrayIt; + typedef typename TChooseClass::Result ArrayType; + typedef typename TChooseClass::Result ItElementType; public: - FORCEINLINE FBaseIterator(const TSparseArray& Array, const TBitArray::FSetBitIterator BitIterator) - : IteratedArray(const_cast&>(Array)) - , BitArrayIt(const_cast(BitIterator)) + explicit TBaseIterator(ArrayType& InArray, const BitArrayItType& InBitArrayIt) + : Array(InArray) + , BitArrayIt(InBitArrayIt) { } - FORCEINLINE explicit operator bool() const - { - return (bool)BitArrayIt; - } - FORCEINLINE TSparseArray::FBaseIterator& operator++() + FORCEINLINE TBaseIterator& operator++() { + // Iterate to the next set allocation flag. ++BitArrayIt; return *this; } - FORCEINLINE ArrayType& operator*() + + FORCEINLINE int32 GetIndex() const { return BitArrayIt.GetIndex(); } + + FORCEINLINE friend bool operator==(const TBaseIterator& Lhs, const TBaseIterator& Rhs) { return Lhs.BitArrayIt == Rhs.BitArrayIt && &Lhs.Array == &Rhs.Array; } + FORCEINLINE friend bool operator!=(const TBaseIterator& Lhs, const TBaseIterator& Rhs) { return Lhs.BitArrayIt != Rhs.BitArrayIt || &Lhs.Array != &Rhs.Array; } + + /** conversion to "bool" returning true if the iterator is valid. */ + FORCEINLINE explicit operator bool() const { - return IteratedArray[BitArrayIt.GetIndex()].ElementData; - } - FORCEINLINE const ArrayType& operator*() const - { - return IteratedArray[BitArrayIt.GetIndex()].ElementData; - } - FORCEINLINE ArrayType* operator->() - { - return &IteratedArray[BitArrayIt.GetIndex()].ElementData; - } - FORCEINLINE const ArrayType* operator->() const - { - return &IteratedArray[BitArrayIt.GetIndex()].ElementData; - } - FORCEINLINE bool operator==(const TSparseArray::FBaseIterator& Other) const - { - return BitArrayIt == Other.BitArrayIt; - } - FORCEINLINE bool operator!=(const TSparseArray::FBaseIterator& Other) const - { - return BitArrayIt != Other.BitArrayIt; + return !!BitArrayIt; } - FORCEINLINE int32 GetIndex() const + /** inverse of the "bool" operator */ + FORCEINLINE bool operator !() const { - return BitArrayIt.GetIndex(); - } - FORCEINLINE bool IsElementValid() const - { - return *BitArrayIt; + return !(bool)*this; } + + FORCEINLINE ItElementType& operator*() const { return Array[GetIndex()]; } + FORCEINLINE ItElementType* operator->() const { return &Array[GetIndex()]; } + FORCEINLINE const FRelativeBitReference& GetRelativeBitReference() const { return BitArrayIt; } + + protected: + ArrayType& Array; + BitArrayItType BitArrayIt; }; public: - FORCEINLINE TSparseArray::FBaseIterator begin() + + /** Iterates over all allocated elements in a sparse array. */ + class TIterator : public TBaseIterator { - return TSparseArray::FBaseIterator(*this, TBitArray::FSetBitIterator(AllocationFlags, 0)); + public: + TIterator(TSparseArray& InArray) + : TBaseIterator(InArray, TConstSetBitIterator(InArray.AllocationFlags)) + { + } + + TIterator(TSparseArray& InArray, const typename TBaseIterator::BitArrayItType& InBitArrayIt) + : TBaseIterator(InArray, InBitArrayIt) + { + } + + /** Safely removes the current element from the array. */ + /* + void RemoveCurrent() + { + this->Array.RemoveAt(this->GetIndex()); + } + */ + }; + + /** Iterates over all allocated elements in a const sparse array. */ + class TConstIterator : public TBaseIterator + { + public: + TConstIterator(const TSparseArray& InArray) + : TBaseIterator(InArray, TConstSetBitIterator(InArray.AllocationFlags)) + { + } + + TConstIterator(const TSparseArray& InArray, const typename TBaseIterator::BitArrayItType& InBitArrayIt) + : TBaseIterator(InArray, InBitArrayIt) + { + } + }; + +#if TSPARSEARRAY_RANGED_FOR_CHECKS // T(R) Milxnor: Check this + class TRangedForIterator : public TIterator + { + public: + TRangedForIterator(TSparseArray& InArray, const typename TBaseIterator::BitArrayItType& InBitArrayIt) + : TIterator(InArray, InBitArrayIt) + , InitialNum(InArray.Num()) + { + } + + private: + int32 InitialNum; + + friend FORCEINLINE bool operator!=(const TRangedForIterator& Lhs, const TRangedForIterator& Rhs) + { + // We only need to do the check in this operator, because no other operator will be + // called until after this one returns. + // + // Also, we should only need to check one side of this comparison - if the other iterator isn't + // even from the same array then the compiler has generated bad code. + ensureMsgf(Lhs.Array.Num() == Lhs.InitialNum, TEXT("Container has changed during ranged-for iteration!")); + return *(TIterator*)&Lhs != *(TIterator*)&Rhs; + } + }; + + class TRangedForConstIterator : public TConstIterator + { + public: + TRangedForConstIterator(const TSparseArray& InArray, const typename TBaseIterator::BitArrayItType& InBitArrayIt) + : TConstIterator(InArray, InBitArrayIt) + , InitialNum(InArray.Num()) + { + } + + private: + int32 InitialNum; + + friend FORCEINLINE bool operator!=(const TRangedForConstIterator& Lhs, const TRangedForConstIterator& Rhs) + { + // We only need to do the check in this operator, because no other operator will be + // called until after this one returns. + // + // Also, we should only need to check one side of this comparison - if the other iterator isn't + // even from the same array then the compiler has generated bad code. + ensureMsgf(Lhs.Array.Num() == Lhs.InitialNum, TEXT("Container has changed during ranged-for iteration!")); + return *(TIterator*)&Lhs != *(TIterator*)&Rhs; + } + }; +#else + using TRangedForIterator = TIterator; + using TRangedForConstIterator = TConstIterator; +#endif + +public: + + ElementType& operator[](int32 Index) + { + // checkSlow(Index >= 0 && Index < Data.Num() && Index < AllocationFlags.Num()); + //checkSlow(AllocationFlags[Index]); // Disabled to improve loading times -BZ + return *(ElementType*)&GetData(Index).ElementData; } - FORCEINLINE const TSparseArray::FBaseIterator begin() const + const ElementType& operator[](int32 Index) const { - return TSparseArray::FBaseIterator(*this, TBitArray::FSetBitIterator(AllocationFlags, 0)); - } - FORCEINLINE TSparseArray::FBaseIterator end() - { - return TSparseArray::FBaseIterator(*this, TBitArray::FSetBitIterator(AllocationFlags)); - } - FORCEINLINE const TSparseArray::FBaseIterator end() const - { - return TSparseArray::FBaseIterator(*this, TBitArray::FSetBitIterator(AllocationFlags)); + // checkSlow(Index >= 0 && Index < Data.Num() && Index < AllocationFlags.Num()); + //checkSlow(AllocationFlags[Index]); // Disabled to improve loading times -BZ + return *(ElementType*)&GetData(Index).ElementData; } - FORCEINLINE FSparseArrayElement& operator[](uint32 Index) + FElementOrFreeListLink& GetData(int32 Index) { - return *(FSparseArrayElement*)&Data.at(Index).ElementData; - } - FORCEINLINE const FSparseArrayElement& operator[](uint32 Index) const - { - return *(const FSparseArrayElement*)&Data.at(Index).ElementData; - } - FORCEINLINE int32 GetNumFreeIndices() const - { - return NumFreeIndices; - } - FORCEINLINE int32 GetFirstFreeIndex() const - { - return FirstFreeIndex; - } - FORCEINLINE const TArray& GetData() const - { - return Data; - } - FSparseArrayElement& GetData(int32 Index) - { - return *(FSparseArrayElement*)&Data.at(Index).ElementData; - // return ((FSparseArrayElement*)Data.Data)[Index]; + return ((FElementOrFreeListLink*)Data.GetData())[Index]; } /** Accessor for the element or free list data. */ - const FSparseArrayElement& GetData(int32 Index) const + const FElementOrFreeListLink& GetData(int32 Index) const { - return *(const FSparseArrayElement*)&Data.at(Index).ElementData; - // return ((FSparseArrayElement*)Data.Data)[Index]; - } - FORCEINLINE const TBitArray& GetAllocationFlags() const - { - return AllocationFlags; - } - FORCEINLINE bool IsIndexValid(int32 IndexToCheck) const - { - return AllocationFlags.IsSet(IndexToCheck); + return ((FElementOrFreeListLink*)Data.GetData())[Index]; } - void RemoveAt(int32 Index, int32 Count = 1) - { - /* if (!TIsTriviallyDestructible::Value) - { - for (int32 It = Index, ItCount = Count; ItCount; ++It, --ItCount) - { - ((ElementType&)GetData(It).ElementData).~ElementType(); - } - } */ + FORCEINLINE friend TRangedForIterator begin(TSparseArray& Array) { return TRangedForIterator(Array, TConstSetBitIterator(Array.AllocationFlags)); } + FORCEINLINE friend TRangedForConstIterator begin(const TSparseArray& Array) { return TRangedForConstIterator(Array, TConstSetBitIterator(Array.AllocationFlags)); } + FORCEINLINE friend TRangedForIterator end(TSparseArray& Array) { return TRangedForIterator(Array, TConstSetBitIterator(Array.AllocationFlags, Array.AllocationFlags.Num())); } + FORCEINLINE friend TRangedForConstIterator end(const TSparseArray& Array) { return TRangedForConstIterator(Array, TConstSetBitIterator(Array.AllocationFlags, Array.AllocationFlags.Num())); } - RemoveAtUninitialized(Index, Count); - } - - /** Removes Count elements from the array, starting from Index, without destructing them. */ - void RemoveAtUninitialized(int32 Index, int32 Count = 1) - { - for (; Count; --Count) - { - // check(AllocationFlags[Index]); - - // Mark the element as free and add it to the free element list. - if (NumFreeIndices) - { - GetData(FirstFreeIndex).PrevFreeIndex = Index; - } - auto& IndexData = GetData(Index); - IndexData.PrevFreeIndex = -1; - IndexData.NextFreeIndex = NumFreeIndices > 0 ? FirstFreeIndex : INDEX_NONE; - FirstFreeIndex = Index; - ++NumFreeIndices; - AllocationFlags.Set(Index, false); - // AllocationFlags[Index] = false; - - ++Index; - } - } - - /* - FORCEINLINE bool RemoveAt(const int32 IndexToRemove) - { - LOG_INFO(LogDev, "IndexToRemove: {}", IndexToRemove); - LOG_INFO(LogDev, "AllocationFlags.IsSet(IndexToRemove): {}", AllocationFlags.IsSet(IndexToRemove)); - LOG_INFO(LogDev, "Data.Num(): {}", Data.Num()); - - if (IndexToRemove >= 0 && IndexToRemove < Data.Num() && AllocationFlags.IsSet(IndexToRemove)) - { - int32 PreviousFreeIndex = -1; - int32 NextFreeIndex = -1; - - LOG_INFO(LogDev, "NumFreeIndices: {}", NumFreeIndices); - - if (NumFreeIndices == 0) - { - FirstFreeIndex = IndexToRemove; - Data.at(IndexToRemove) = { -1, -1 }; - } - else - { - for (auto It = AllocationFlags.begin(); It != AllocationFlags.end(); ++It) - { - if (!It) - { - if (It.GetIndex() < IndexToRemove) - { - Data.at(IndexToRemove).PrevFreeIndex = It.GetIndex(); - } - else if (It.GetIndex() > IndexToRemove) - { - Data.at(IndexToRemove).NextFreeIndex = It.GetIndex(); - break; - } - } - } - } - - AllocationFlags.Set(IndexToRemove, false); - NumFreeIndices++; - - return true; - } - return false; - } - */ }; \ No newline at end of file diff --git a/Project Reboot 3.0/TimerManager.h b/Project Reboot 3.0/TimerManager.h index aabe2b8..085b891 100644 --- a/Project Reboot 3.0/TimerManager.h +++ b/Project Reboot 3.0/TimerManager.h @@ -16,7 +16,7 @@ struct FTimerUnifiedDelegate TFunction FuncCallback; FTimerUnifiedDelegate() {}; - FTimerUnifiedDelegate(FTimerDelegate const& D) : FuncDelegate(D) {}; + // FTimerUnifiedDelegate(FTimerDelegate const& D) : FuncDelegate(D) {}; // T(R) }; class FTimerManager // : public FNoncopyable @@ -27,6 +27,6 @@ public: static void (*InternalSetTimerOriginal)(__int64 TimerManager, FTimerHandle& InOutHandle, FTimerUnifiedDelegate&& InDelegate, float InRate, bool InbLoop, float InFirstDelay) = decltype(InternalSetTimerOriginal)(Addresses::SetTimer); - InternalSetTimerOriginal(__int64(this), InOutHandle, FTimerUnifiedDelegate(InDelegate), InRate, InbLoop, InFirstDelay); + // InternalSetTimerOriginal(__int64(this), InOutHandle, FTimerUnifiedDelegate(InDelegate), InRate, InbLoop, InFirstDelay); // T(R) } }; \ No newline at end of file diff --git a/Project Reboot 3.0/UnrealMathUtility.h b/Project Reboot 3.0/UnrealMathUtility.h index 41660ab..e21ba28 100644 --- a/Project Reboot 3.0/UnrealMathUtility.h +++ b/Project Reboot 3.0/UnrealMathUtility.h @@ -16,6 +16,93 @@ struct FMath : public FGenericPlatformMath return A * A; } + static float SRand() // T(REP) + { + return rand(); + } + + static FORCEINLINE uint32 FloorLog2(uint32 Value) + { + /* // reference implementation + // 1500ms on test data + uint32 Bit = 32; + for (; Bit > 0;) + { + Bit--; + if (Value & (1<= 1 << 16) { Value >>= 16; pos += 16; } + if (Value >= 1 << 8) { Value >>= 8; pos += 8; } + if (Value >= 1 << 4) { Value >>= 4; pos += 4; } + if (Value >= 1 << 2) { Value >>= 2; pos += 2; } + if (Value >= 1 << 1) { pos += 1; } + return (Value == 0) ? 0 : pos; + + // even faster would be method3 but it can introduce more cache misses and it would need to store the table somewhere + // 304ms in test data + /*int LogTable256[256]; + + void prep() + { + LogTable256[0] = LogTable256[1] = 0; + for (int i = 2; i < 256; i++) + { + LogTable256[i] = 1 + LogTable256[i / 2]; + } + LogTable256[0] = -1; // if you want log(0) to return -1 + } + + int _forceinline method3(uint32 v) + { + int r; // r will be lg(v) + uint32 tt; // temporaries + + if ((tt = v >> 24) != 0) + { + r = (24 + LogTable256[tt]); + } + else if ((tt = v >> 16) != 0) + { + r = (16 + LogTable256[tt]); + } + else if ((tt = v >> 8 ) != 0) + { + r = (8 + LogTable256[tt]); + } + else + { + r = LogTable256[v]; + } + return r; + }*/ + } + + static FORCEINLINE uint32 CountLeadingZeros(uint32 Value) + { + if (Value == 0) return 32; + return 31 - FloorLog2(Value); + } + + template + static constexpr FORCEINLINE T DivideAndRoundUp(T Dividend, T Divisor) + { + return (Dividend + Divisor - 1) / Divisor; + } + + static FORCEINLINE int32 Rand() { return rand(); } + + static FORCEINLINE float FRand() { return Rand() / (float)RAND_MAX; } + #define FASTASIN_HALF_PI (1.5707963050f) /** * Computes the ASin of a scalar value. diff --git a/Project Reboot 3.0/UnrealTypeTraits.h b/Project Reboot 3.0/UnrealTypeTraits.h index 220af6a..1147731 100644 --- a/Project Reboot 3.0/UnrealTypeTraits.h +++ b/Project Reboot 3.0/UnrealTypeTraits.h @@ -26,6 +26,14 @@ struct TCallTraitsParamTypeHelper typedef const T* ConstParamType; }; +template struct TContainerTraitsBase +{ + // This should be overridden by every container that supports emptying its contents via a move operation. + enum { MoveWillEmptyContainer = false }; +}; + +template struct TContainerTraits : public TContainerTraitsBase {}; + template struct TCallTraitsBase { diff --git a/Project Reboot 3.0/Vector.h b/Project Reboot 3.0/Vector.h index d3b5d17..2f3f4bd 100644 --- a/Project Reboot 3.0/Vector.h +++ b/Project Reboot 3.0/Vector.h @@ -2,6 +2,12 @@ #include "inc.h" +enum EForceInit +{ + ForceInit, + ForceInitToZero +}; + struct FVector { public: @@ -62,4 +68,10 @@ public: { *this = *this - A; } + + explicit FORCEINLINE FVector(EForceInit) + : X(0.0f), Y(0.0f), Z(0.0f) + { + // DiagnosticCheckNaN(); + } }; \ No newline at end of file diff --git a/Project Reboot 3.0/WeakObjectPtr.h b/Project Reboot 3.0/WeakObjectPtr.h index 7496a5a..09b3e42 100644 --- a/Project Reboot 3.0/WeakObjectPtr.h +++ b/Project Reboot 3.0/WeakObjectPtr.h @@ -13,6 +13,19 @@ public: return ChunkedObjects ? ChunkedObjects->GetObjectByIndex(ObjectIndex) : UnchunkedObjects ? UnchunkedObjects->GetObjectByIndex(ObjectIndex) : nullptr; } + FORCEINLINE bool operator==(const FWeakObjectPtr& Other) const + { + return + (ObjectIndex == Other.ObjectIndex && ObjectSerialNumber == Other.ObjectSerialNumber) + // || (!IsValid() && !Other.IsValid()) + ; + } + + friend uint32 GetTypeHash(const FWeakObjectPtr& WeakObjectPtr) + { + return uint32(WeakObjectPtr.ObjectIndex ^ WeakObjectPtr.ObjectSerialNumber); + } + bool operator==(const FWeakObjectPtr& other) { return ObjectIndex == other.ObjectIndex && ObjectSerialNumber == other.ObjectSerialNumber; diff --git a/Project Reboot 3.0/WeakObjectPtrTemplates.h b/Project Reboot 3.0/WeakObjectPtrTemplates.h index 8807d3e..ec75e48 100644 --- a/Project Reboot 3.0/WeakObjectPtrTemplates.h +++ b/Project Reboot 3.0/WeakObjectPtrTemplates.h @@ -2,6 +2,7 @@ #include "WeakObjectPtr.h" #include "Object.h" +#include "PointerIsConvertibleFromTo.h" template struct TWeakObjectPtr; @@ -14,8 +15,48 @@ struct TWeakObjectPtr : public TWeakObjectPtrBase return (T*)TWeakObjectPtrBase::Get(); } + /* bool operator==(const TWeakObjectPtr& other) { return TWeakObjectPtrBase::operator==(other); } -}; \ No newline at end of file + */ + + TWeakObjectPtr() {} +}; + +template +FORCENOINLINE bool operator==(const TWeakObjectPtr& Lhs, const RhsT* Rhs) +{ + // It's also possible that these static_asserts may fail for valid conversions because + // one or both of the types have only been forward-declared. + static_assert(TPointerIsConvertibleFromTo::Value, "TWeakObjectPtr can only be compared with UObject types"); + static_assert(TPointerIsConvertibleFromTo::Value || TPointerIsConvertibleFromTo::Value, "Unable to compare TWeakObjectPtr with raw pointer - types are incompatible"); + + // NOTE: this constructs a TWeakObjectPtrBase, which has some amount of overhead, so this may not be an efficient operation + return (const OtherTWeakObjectPtrBase&)Lhs == OtherTWeakObjectPtrBase(Rhs); +} + +template +FORCENOINLINE bool operator==(const LhsT* Lhs, const TWeakObjectPtr& Rhs) +{ + // It's also possible that these static_asserts may fail for valid conversions because + // one or both of the types have only been forward-declared. + static_assert(TPointerIsConvertibleFromTo::Value, "TWeakObjectPtr can only be compared with UObject types"); + static_assert(TPointerIsConvertibleFromTo::Value || TPointerIsConvertibleFromTo::Value, "Unable to compare TWeakObjectPtr with raw pointer - types are incompatible"); + + // NOTE: this constructs a TWeakObjectPtrBase, which has some amount of overhead, so this may not be an efficient operation + return OtherTWeakObjectPtrBase(Lhs) == (const OtherTWeakObjectPtrBase&)Rhs; +} + +template +FORCENOINLINE bool operator==(const TWeakObjectPtr& Lhs, TYPE_OF_NULLPTR) +{ + return !Lhs.IsValid(); +} + +template +FORCENOINLINE bool operator==(TYPE_OF_NULLPTR, const TWeakObjectPtr& Rhs) +{ + return !Rhs.IsValid(); +} \ No newline at end of file diff --git a/Project Reboot 3.0/World.h b/Project Reboot 3.0/World.h index 10fe581..0c9473e 100644 --- a/Project Reboot 3.0/World.h +++ b/Project Reboot 3.0/World.h @@ -6,17 +6,14 @@ #include "Rotator.h" #include "Actor.h" #include "GameInstance.h" +#include "WorldSettings.h" +#include "GameplayStatics.h" struct FNetworkNotify { }; -class AWorldSettings : public AActor -{ -public: -}; - struct FActorSpawnParameters { FName Name = FName(0); @@ -228,6 +225,11 @@ public: return this->Get(NetDriverOffset); } + float GetTimeSeconds() // T(REP) + { + return UGameplayStatics::GetTimeSeconds(this); + } + UGameInstance* GetOwningGameInstance() { static auto OwningGameInstanceOffset = GetOffset("OwningGameInstance"); diff --git a/Project Reboot 3.0/WorldSettings.h b/Project Reboot 3.0/WorldSettings.h new file mode 100644 index 0000000..f3d29e4 --- /dev/null +++ b/Project Reboot 3.0/WorldSettings.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Actor.h" +#include "NetConnection.h" +#include "Array.h" + +struct FNetViewer +{ + UNetConnection* Connection; + AActor* InViewer; + AActor* ViewTarget; + FVector ViewLocation; + FVector ViewDir; + + FNetViewer() + : Connection(NULL) + , InViewer(NULL) + , ViewTarget(NULL) + , ViewLocation(ForceInit) + , ViewDir(ForceInit) + { + } + + FNetViewer(UNetConnection* InConnection, float DeltaSeconds); +}; + +class AWorldSettings : public AActor +{ +public: + TArray& GetReplicationViewers() + { + static auto ReplicationViewersOffset = GetOffset("ReplicationViewers"); + return Get>(ReplicationViewersOffset); + } +}; diff --git a/Project Reboot 3.0/addresses.cpp b/Project Reboot 3.0/addresses.cpp index 8653875..afec44a 100644 --- a/Project Reboot 3.0/addresses.cpp +++ b/Project Reboot 3.0/addresses.cpp @@ -553,6 +553,12 @@ void Addresses::Init() NavSystemCleanUpOriginal = decltype(NavSystemCleanUpOriginal)(Addresses::NavSystemCleanUp); LoadPlaysetOriginal = decltype(LoadPlaysetOriginal)(Addresses::LoadPlayset); AFortGameModeAthena::SetZoneToIndexOriginal = decltype(AFortGameModeAthena::SetZoneToIndexOriginal)(Addresses::SetZoneToIndex); + AActor::originalCallPreReplication = decltype(AActor::originalCallPreReplication)(Addresses::CallPreReplication); + APlayerController::originalSendClientAdjustment = decltype(APlayerController::originalSendClientAdjustment)(Addresses::SendClientAdjustment); + UActorChannel::originalReplicateActor = decltype(UActorChannel::originalReplicateActor)(Addresses::ReplicateActor); + UActorChannel::originalSetChannelActor = decltype(UActorChannel::originalSetChannelActor)(Addresses::SetChannelActor); + UNetConnection::originalCreateChannel = decltype(UNetConnection::originalCreateChannel)(Addresses::CreateChannel); + UNetConnection::originalCreateChannelByName = decltype(UNetConnection::originalCreateChannelByName)(Addresses::CreateChannel); if (Engine_Version >= 421) ChunkedObjects = decltype(ChunkedObjects)(ObjectArray); else UnchunkedObjects = decltype(UnchunkedObjects)(ObjectArray); diff --git a/Project Reboot 3.0/ai.h b/Project Reboot 3.0/ai.h index 5931cdf..d74459a 100644 --- a/Project Reboot 3.0/ai.h +++ b/Project Reboot 3.0/ai.h @@ -110,11 +110,29 @@ static inline AFortAthenaMutator_Bots* SpawnBotMutator() //sets up all the class auto GameState = Cast(GetWorld()->GetGameState()); auto GameMode = Cast(GetWorld()->GetGameMode()); + auto MutatorBotsClass = FindObject("/Script/FortniteGame.FortAthenaMutator_Bots"); + + if (!MutatorBotsClass) + { + return nullptr; + } + static auto BGAClass = FindObject(L"/Script/Engine.BlueprintGeneratedClass"); static auto PhoebeMutatorClass = LoadObject(L"/Game/Athena/AI/Phoebe/BP_Phoebe_Mutator.BP_Phoebe_Mutator_C", BGAClass); + if (!PhoebeMutatorClass) + { + return nullptr; + } + auto BotMutator = GetWorld()->SpawnActor(PhoebeMutatorClass); + if (!BotMutator) + { + LOG_WARN(LogAI, "Failed to spawn Bot Mutator!"); + return nullptr; + } + static auto CachedGameModeOffset = BotMutator->GetOffset("CachedGameMode"); BotMutator->Get(CachedGameModeOffset) = GameMode; diff --git a/Project Reboot 3.0/inc.h b/Project Reboot 3.0/inc.h index 7ff98e1..3b652ea 100644 --- a/Project Reboot 3.0/inc.h +++ b/Project Reboot 3.0/inc.h @@ -5,6 +5,16 @@ #include #include +/* + +T(REP): Todo replication +T(R): Todo random (every commented check, checkslow is automatically this) +T(1-5): 1, being most important, 5 being least important to fix. + +*/ + +decltype(nullptr) typedef TYPE_OF_NULLPTR; + typedef unsigned short uint16; typedef unsigned char uint8; typedef char int8; @@ -25,6 +35,8 @@ extern inline int Fortnite_CL = 0; // #define ABOVE_S20 +#define INDEX_NONE -1 + struct PlaceholderBitfield { uint8_t First : 1; @@ -62,6 +74,15 @@ inline bool IsRestartingSupported() return Engine_Version >= 419 && Engine_Version < 424; } +enum ENetRole +{ + ROLE_None, + ROLE_SimulatedProxy, + ROLE_AutonomousProxy, + ROLE_Authority, + ROLE_MAX, +}; + /* enum class AllocatorType diff --git a/Project Reboot 3.0/reboot.h b/Project Reboot 3.0/reboot.h index d7cbaf1..6fc5f68 100644 --- a/Project Reboot 3.0/reboot.h +++ b/Project Reboot 3.0/reboot.h @@ -80,15 +80,17 @@ static inline UEngine* GetEngine() return Engine; } -static inline class UWorld* GetWorld() -{ - static UObject* Engine = GetEngine(); - static auto GameViewportOffset = Engine->GetOffset("GameViewport"); - auto GameViewport = Engine->Get(GameViewportOffset); +namespace { + static inline class UWorld* GetWorld() + { + static UObject* Engine = GetEngine(); + static auto GameViewportOffset = Engine->GetOffset("GameViewport"); + auto GameViewport = Engine->Get(GameViewportOffset); - static auto WorldOffset = GameViewport->GetOffset("World"); + static auto WorldOffset = GameViewport->GetOffset("World"); - return GameViewport->Get(WorldOffset); + return GameViewport->Get(WorldOffset); + } } static TArray& GetLocalPlayers() @@ -406,7 +408,8 @@ namespace MemberOffsets } } -static inline float GetMaxTickRateHook() { return 30.f; } +inline float ServerTickRate = 30.f; +static inline float GetMaxTickRateHook() { return ServerTickRate; } #define VALIDATEOFFSET(offset) if (!offset) LOG_WARN(LogDev, "[{}] Invalid offset", __FUNCTIONNAME__);