From cb57a1b84304d66e3b957353d79097cafd6059fe Mon Sep 17 00:00:00 2001 From: Milxnor Date: Mon, 3 Apr 2023 02:04:29 -0400 Subject: [PATCH] too much stuff fix s3, fix health + backpack bug on 1.11, change replciation, fix 7.20 & 12.61, fix backpack on >S9, probably some other stuff i forgot --- Project Reboot 3.0/Actor.cpp | 22 ++ Project Reboot 3.0/Actor.h | 4 + Project Reboot 3.0/BitArray.h | 316 +++++++++++++++++- .../ContainerAllocationPolicies.h | 42 +++ Project Reboot 3.0/FortGameModeAthena.cpp | 51 +-- Project Reboot 3.0/FortInventory.cpp | 2 +- Project Reboot 3.0/FortLootPackage.cpp | 165 ++++----- Project Reboot 3.0/FortPlayerController.cpp | 17 +- Project Reboot 3.0/FortPlayerController.h | 17 + .../FortPlayerControllerAthena.cpp | 46 ++- .../FortPlayerControllerAthena.h | 6 +- Project Reboot 3.0/FortPlayerPawn.cpp | 4 +- Project Reboot 3.0/FortPlayerPawn.h | 2 +- Project Reboot 3.0/Level.cpp | 25 ++ Project Reboot 3.0/Level.h | 10 + Project Reboot 3.0/NetDriver.cpp | 193 +++++++++-- Project Reboot 3.0/NetDriver.h | 68 ++++ Project Reboot 3.0/NetworkObjectList.cpp | 107 ++++++ Project Reboot 3.0/Object.cpp | 5 + Project Reboot 3.0/Object.h | 21 +- Project Reboot 3.0/ObjectMacros.h | 1 + Project Reboot 3.0/Project Reboot 3.0.vcxproj | 5 + .../Project Reboot 3.0.vcxproj.filters | 21 +- Project Reboot 3.0/Set.h | 170 +++++++++- Project Reboot 3.0/SharedPointer.h | 36 ++ Project Reboot 3.0/SparseArray.h | 139 +++++++- Project Reboot 3.0/UObjectArray.h | 5 + Project Reboot 3.0/WeakObjectPtr.h | 7 + Project Reboot 3.0/WeakObjectPtrTemplates.h | 16 + Project Reboot 3.0/addresses.cpp | 2 +- Project Reboot 3.0/dllmain.cpp | 143 ++++++-- Project Reboot 3.0/finder.h | 5 +- 32 files changed, 1457 insertions(+), 216 deletions(-) create mode 100644 Project Reboot 3.0/Level.cpp create mode 100644 Project Reboot 3.0/Level.h create mode 100644 Project Reboot 3.0/NetworkObjectList.cpp create mode 100644 Project Reboot 3.0/SharedPointer.h create mode 100644 Project Reboot 3.0/WeakObjectPtrTemplates.h diff --git a/Project Reboot 3.0/Actor.cpp b/Project Reboot 3.0/Actor.cpp index 7cae305..cdcaf41 100644 --- a/Project Reboot 3.0/Actor.cpp +++ b/Project Reboot 3.0/Actor.cpp @@ -126,6 +126,28 @@ void AActor::ForceNetUpdate() this->ProcessEvent(ForceNetUpdateFn); } +bool AActor::IsNetStartupActor() +{ + return IsNetStartup(); // The implementation on this function depends on the version. +} + +bool AActor::IsPendingKillPending() +{ + return IsActorBeingDestroyed() || !IsValidChecked(this); +} + +float& AActor::GetNetUpdateFrequency() +{ + static auto NetUpdateFrequencyOffset = GetOffset("NetUpdateFrequency"); + return Get(NetUpdateFrequencyOffset); +} + +float& AActor::GetMinNetUpdateFrequency() +{ + static auto MinNetUpdateFrequencyOffset = GetOffset("MinNetUpdateFrequency"); + return Get(MinNetUpdateFrequencyOffset); +} + bool AActor::IsAlwaysRelevant() { static auto bAlwaysRelevantOffset = GetOffset("bAlwaysRelevant"); diff --git a/Project Reboot 3.0/Actor.h b/Project Reboot 3.0/Actor.h index fd93499..ee6b5c1 100644 --- a/Project Reboot 3.0/Actor.h +++ b/Project Reboot 3.0/Actor.h @@ -25,6 +25,10 @@ public: void SetCanBeDamaged(bool NewValue); void SetOwner(AActor* Owner); void ForceNetUpdate(); + bool IsNetStartupActor(); + bool IsPendingKillPending(); + float& GetNetUpdateFrequency(); + float& GetMinNetUpdateFrequency(); static class UClass* StaticClass(); }; \ No newline at end of file diff --git a/Project Reboot 3.0/BitArray.h b/Project Reboot 3.0/BitArray.h index 8d44396..9084c56 100644 --- a/Project Reboot 3.0/BitArray.h +++ b/Project Reboot 3.0/BitArray.h @@ -2,6 +2,20 @@ #include "ContainerAllocationPolicies.h" +static FORCEINLINE uint32 CountLeadingZeros(uint32 Value) +{ + unsigned long Log2; + if (_BitScanReverse(&Log2, Value) != 0) + { + return 31 - Log2; + } + + return 32; +} + +#define NumBitsPerDWORD ((int32)32) +#define NumBitsPerDWORDLogTwo ((int32)5) + class TBitArray { private: @@ -10,8 +24,308 @@ private: template friend class TSet; -private: +public: TInlineAllocator<4>::ForElementType Data; int NumBits; int MaxBits; + + struct FRelativeBitReference + { + public: + FORCEINLINE explicit FRelativeBitReference(int32 BitIndex) + : DWORDIndex(BitIndex >> NumBitsPerDWORDLogTwo) + , Mask(1 << (BitIndex & ((NumBitsPerDWORD)-1))) + { + } + + int32 DWORDIndex; + uint32 Mask; + }; + +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; + }; + +public: + class FBitIterator : public FRelativeBitReference + { + private: + int32 Index; + const TBitArray& IteratedArray; + + 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; + } + } + }; + +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); + } + + 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); + } + + 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))); + + if (!bIsSettingAllZero) + NumBits = Index >= NumBits ? Index < MaxBits ? Index + 1 : NumBits : NumBits; + + 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 diff --git a/Project Reboot 3.0/ContainerAllocationPolicies.h b/Project Reboot 3.0/ContainerAllocationPolicies.h index 8bc5f96..11695bb 100644 --- a/Project Reboot 3.0/ContainerAllocationPolicies.h +++ b/Project Reboot 3.0/ContainerAllocationPolicies.h @@ -25,5 +25,47 @@ public: 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]; + } }; }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortGameModeAthena.cpp b/Project Reboot 3.0/FortGameModeAthena.cpp index a6dbabc..bbaa0a3 100644 --- a/Project Reboot 3.0/FortGameModeAthena.cpp +++ b/Project Reboot 3.0/FortGameModeAthena.cpp @@ -283,10 +283,12 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game else { if (Fortnite_Version >= 4) + { SetPlaylist(PlaylistToUse, true); - auto CurrentPlaylist = GameState->GetCurrentPlaylist(); - LOG_INFO(LogDev, "Set playlist to {}!", CurrentPlaylist->IsValidLowLevel() ? CurrentPlaylist->GetFullName() : "Invalid"); + auto CurrentPlaylist = GameState->GetCurrentPlaylist(); + LOG_INFO(LogDev, "Set playlist to {}!", CurrentPlaylist->IsValidLowLevel() ? CurrentPlaylist->GetFullName() : "Invalid"); + } } // if (false) @@ -424,15 +426,17 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game } } - auto PlaylistToUse = GetPlaylistToUse(); + static auto CurrentPlaylistDataOffset = GameState->GetOffset("CurrentPlaylistData", false); - if (PlaylistToUse) + auto CurrentPlaylist = CurrentPlaylistDataOffset == -1 && Fortnite_Version < 6 ? nullptr : GameState->GetCurrentPlaylist(); + + if (CurrentPlaylist) { - static auto AdditionalLevelsOffset = PlaylistToUse->GetOffset("AdditionalLevels", false); + static auto AdditionalLevelsOffset = CurrentPlaylist->GetOffset("AdditionalLevels", false); if (AdditionalLevelsOffset != -1) { - auto& AdditionalLevels = PlaylistToUse->Get>>(AdditionalLevelsOffset); + auto& AdditionalLevels = CurrentPlaylist->Get>>(AdditionalLevelsOffset); LOG_INFO(LogPlaylist, "Loading {} playlist levels.", AdditionalLevels.Num()); @@ -525,6 +529,10 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game float TimeSeconds = UGameplayStatics::GetTimeSeconds(GetWorld()); LOG_INFO(LogDev, "Initializing!"); + + if (std::floor(Fortnite_Version) == 3) + SetPlaylist(GetPlaylistToUse(), true); + LOG_INFO(LogDev, "GameMode 0x{:x}", __int64(GameMode)); GameState->Get("WarmupCountdownEndTime") = TimeSeconds + Duration; @@ -539,26 +547,6 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game GameSession->Get(MaxPlayersOffset) = 100; - /* - auto AllMegaStormManagers = UGameplayStatics::GetAllActorsOfClass(GetWorld(), GameMode->Get("MegaStormManagerClass")); - - LOG_INFO(LogDev, "AllMegaStormManagers.Num() {}", AllMegaStormManagers.Num()); - - if (AllMegaStormManagers.Num()) - { - auto MegaStormManager = (AMegaStormManager*)AllMegaStormManagers.at(0); // GameMode->Get(MegaStormManagerOffset); - - LOG_INFO(LogDev, "MegaStormManager {}", __int64(MegaStormManager)); - - if (MegaStormManager) - { - LOG_INFO(LogDev, "MegaStormManager->GetMegaStormCircles().Num() {}", MegaStormManager->GetMegaStormCircles().Num()); - } - } - */ - - // GameState->Get("bGameModeWillSkipAircraft") = Globals::bGoingToPlayEvent && Fortnite_Version == 17.30; - // if (Engine_Version < 424) GameState->OnRep_CurrentPlaylistInfo(); // ? @@ -807,8 +795,8 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena Parts[(int)EFortCustomPartType::Head] = headPart; Parts[(int)EFortCustomPartType::Body] = bodyPart; - if (Fortnite_Version > 2.5) - Parts[(int)EFortCustomPartType::Backpack] = backpackPart; + // if (Fortnite_Version > 2.5) + Parts[(int)EFortCustomPartType::Backpack] = backpackPart; static auto OnRep_CharacterPartsFn = FindObject("/Script/FortniteGame.FortPlayerState.OnRep_CharacterParts"); PlayerStateAthena->ProcessEvent(OnRep_CharacterPartsFn); @@ -865,16 +853,9 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena { UClass* AbilityClass = GameplayAbilities->At(i); - // LOG_INFO(LogDev, "AbilityClass {}", __int64(AbilityClass)); - if (!AbilityClass) continue; - // LOG_INFO(LogDev, "AbilityClass Name {}", AbilityClass->GetFullName()); - - // LOG_INFO(LogDev, "DefaultAbility {}", __int64(DefaultAbility)); - // LOG_INFO(LogDev, "DefaultAbility Name {}", DefaultAbility->GetFullName()); - PlayerStateAthena->GetAbilitySystemComponent()->GiveAbilityEasy(AbilityClass); } } diff --git a/Project Reboot 3.0/FortInventory.cpp b/Project Reboot 3.0/FortInventory.cpp index 9574008..714fa53 100644 --- a/Project Reboot 3.0/FortInventory.cpp +++ b/Project Reboot 3.0/FortInventory.cpp @@ -23,7 +23,7 @@ std::pair, std::vector> AFortInventory::AddI if (LoadedAmmo == -1) { - if (auto WeaponDef = Cast(ItemDefinition)) + if (auto WeaponDef = Cast(ItemDefinition)) // bPreventDefaultPreload ? LoadedAmmo = WeaponDef->GetClipSize(); else LoadedAmmo = 0; diff --git a/Project Reboot 3.0/FortLootPackage.cpp b/Project Reboot 3.0/FortLootPackage.cpp index 3677064..13af7dc 100644 --- a/Project Reboot 3.0/FortLootPackage.cpp +++ b/Project Reboot 3.0/FortLootPackage.cpp @@ -229,125 +229,128 @@ std::vector PickLootDrops(FName TierGroupName, bool bPrint, int recurs auto GameFeatureData = Object; static auto DefaultLootTableDataOffset = GameFeatureData->GetOffset("DefaultLootTableData"); - auto DefaultLootTableData = GameFeatureData->GetPtr(DefaultLootTableDataOffset); - - auto LootTierDataTableStr = DefaultLootTableData->LootTierData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); - - auto LootTierDataTableIsComposite = LootTierDataTableStr.contains("Composite"); - auto LootPackageTableStr = DefaultLootTableData->LootPackageData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); - auto LootPackageTableIsComposite = LootPackageTableStr.contains("Composite"); - - auto LootTierDataPtr = DefaultLootTableData->LootTierData.Get(LootTierDataTableIsComposite ? CompositeDataTableClass : DataTableClass, true); - auto LootPackagePtr = DefaultLootTableData->LootPackageData.Get(LootPackageTableIsComposite ? CompositeDataTableClass : DataTableClass, true); - - if (LootPackagePtr) + if (DefaultLootTableDataOffset != -1) { - LPTables.push_back(LootPackagePtr); - } + auto DefaultLootTableData = GameFeatureData->GetPtr(DefaultLootTableDataOffset); - if (CurrentPlaylist) - { - static auto PlaylistOverrideLootTableDataOffset = GameFeatureData->GetOffset("PlaylistOverrideLootTableData"); - auto PlaylistOverrideLootTableData = GameFeatureData->GetPtr>(PlaylistOverrideLootTableDataOffset); + auto LootTierDataTableStr = DefaultLootTableData->LootTierData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); - auto PlaylistOverrideLootTableData_Data = PlaylistOverrideLootTableData->Pairs.Elements.Data; + auto LootTierDataTableIsComposite = LootTierDataTableStr.contains("Composite"); + auto LootPackageTableStr = DefaultLootTableData->LootPackageData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); + auto LootPackageTableIsComposite = LootPackageTableStr.contains("Composite"); - static auto GameplayTagContainerOffset = CurrentPlaylist->GetOffset("GameplayTagContainer"); - auto GameplayTagContainer = CurrentPlaylist->GetPtr(GameplayTagContainerOffset); + auto LootTierDataPtr = DefaultLootTableData->LootTierData.Get(LootTierDataTableIsComposite ? CompositeDataTableClass : DataTableClass, true); + auto LootPackagePtr = DefaultLootTableData->LootPackageData.Get(LootPackageTableIsComposite ? CompositeDataTableClass : DataTableClass, true); - for (int i = 0; i < GameplayTagContainer->GameplayTags.Num(); i++) + if (LootPackagePtr) { - auto& Tag = GameplayTagContainer->GameplayTags.At(i); + LPTables.push_back(LootPackagePtr); + } - for (int j = 0; j < PlaylistOverrideLootTableData_Data.Num(); j++) + if (CurrentPlaylist) + { + static auto PlaylistOverrideLootTableDataOffset = GameFeatureData->GetOffset("PlaylistOverrideLootTableData"); + auto PlaylistOverrideLootTableData = GameFeatureData->GetPtr>(PlaylistOverrideLootTableDataOffset); + + auto PlaylistOverrideLootTableData_Data = PlaylistOverrideLootTableData->Pairs.Elements.Data; + + static auto GameplayTagContainerOffset = CurrentPlaylist->GetOffset("GameplayTagContainer"); + auto GameplayTagContainer = CurrentPlaylist->GetPtr(GameplayTagContainerOffset); + + for (int i = 0; i < GameplayTagContainer->GameplayTags.Num(); i++) { - auto Value = PlaylistOverrideLootTableData_Data.at(j).ElementData.Value; - auto CurrentOverrideTag = Value.First; + auto& Tag = GameplayTagContainer->GameplayTags.At(i); - if (Tag.TagName == CurrentOverrideTag.TagName) + for (int j = 0; j < PlaylistOverrideLootTableData_Data.Num(); j++) { - auto OverrideLootPackageTableStr = Value.Second.LootPackageData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); - auto bOverrideIsComposite = OverrideLootPackageTableStr.contains("Composite"); + auto Value = PlaylistOverrideLootTableData_Data.at(j).ElementData.Value; + auto CurrentOverrideTag = Value.First; - auto ptr = Value.Second.LootPackageData.Get(bOverrideIsComposite ? CompositeDataTableClass : DataTableClass, true); - - if (ptr) + if (Tag.TagName == CurrentOverrideTag.TagName) { - if (bOverrideIsComposite) + auto OverrideLootPackageTableStr = Value.Second.LootPackageData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); + auto bOverrideIsComposite = OverrideLootPackageTableStr.contains("Composite"); + + auto ptr = Value.Second.LootPackageData.Get(bOverrideIsComposite ? CompositeDataTableClass : DataTableClass, true); + + if (ptr) { - static auto ParentTablesOffset = ptr->GetOffset("ParentTables"); - - auto ParentTables = ptr->GetPtr>(ParentTablesOffset); - - for (int z = 0; z < ParentTables->size(); z++) + if (bOverrideIsComposite) { - auto ParentTable = ParentTables->At(z); + static auto ParentTablesOffset = ptr->GetOffset("ParentTables"); - if (ParentTable) + auto ParentTables = ptr->GetPtr>(ParentTablesOffset); + + for (int z = 0; z < ParentTables->size(); z++) { - LPTables.push_back(ParentTable); + auto ParentTable = ParentTables->At(z); + + if (ParentTable) + { + LPTables.push_back(ParentTable); + } } } - } - LPTables.push_back(ptr); + LPTables.push_back(ptr); + } } } } } - } - if (LootTierDataPtr) - { - LTDTables.push_back(LootTierDataPtr); - } - - if (CurrentPlaylist) - { - static auto PlaylistOverrideLootTableDataOffset = GameFeatureData->GetOffset("PlaylistOverrideLootTableData"); - auto PlaylistOverrideLootTableData = GameFeatureData->GetPtr>(PlaylistOverrideLootTableDataOffset); - - auto PlaylistOverrideLootTableData_Data = PlaylistOverrideLootTableData->Pairs.Elements.Data; - - static auto GameplayTagContainerOffset = CurrentPlaylist->GetOffset("GameplayTagContainer"); - auto GameplayTagContainer = CurrentPlaylist->GetPtr(GameplayTagContainerOffset); - - for (int i = 0; i < GameplayTagContainer->GameplayTags.Num(); i++) + if (LootTierDataPtr) { - auto& Tag = GameplayTagContainer->GameplayTags.At(i); + LTDTables.push_back(LootTierDataPtr); + } - for (int j = 0; j < PlaylistOverrideLootTableData_Data.Num(); j++) + if (CurrentPlaylist) + { + static auto PlaylistOverrideLootTableDataOffset = GameFeatureData->GetOffset("PlaylistOverrideLootTableData"); + auto PlaylistOverrideLootTableData = GameFeatureData->GetPtr>(PlaylistOverrideLootTableDataOffset); + + auto PlaylistOverrideLootTableData_Data = PlaylistOverrideLootTableData->Pairs.Elements.Data; + + static auto GameplayTagContainerOffset = CurrentPlaylist->GetOffset("GameplayTagContainer"); + auto GameplayTagContainer = CurrentPlaylist->GetPtr(GameplayTagContainerOffset); + + for (int i = 0; i < GameplayTagContainer->GameplayTags.Num(); i++) { - auto Value = PlaylistOverrideLootTableData_Data.at(j).ElementData.Value; - auto CurrentOverrideTag = Value.First; + auto& Tag = GameplayTagContainer->GameplayTags.At(i); - if (Tag.TagName == CurrentOverrideTag.TagName) + for (int j = 0; j < PlaylistOverrideLootTableData_Data.Num(); j++) { - auto OverrideLootTierDataStr = Value.Second.LootTierData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); - auto bOverrideIsComposite = OverrideLootTierDataStr.contains("Composite"); + auto Value = PlaylistOverrideLootTableData_Data.at(j).ElementData.Value; + auto CurrentOverrideTag = Value.First; - auto ptr = Value.Second.LootTierData.Get(bOverrideIsComposite ? CompositeDataTableClass : DataTableClass, true); - - if (ptr) + if (Tag.TagName == CurrentOverrideTag.TagName) { - if (bOverrideIsComposite) + auto OverrideLootTierDataStr = Value.Second.LootTierData.SoftObjectPtr.ObjectID.AssetPathName.ToString(); + auto bOverrideIsComposite = OverrideLootTierDataStr.contains("Composite"); + + auto ptr = Value.Second.LootTierData.Get(bOverrideIsComposite ? CompositeDataTableClass : DataTableClass, true); + + if (ptr) { - static auto ParentTablesOffset = ptr->GetOffset("ParentTables"); - - auto ParentTables = ptr->GetPtr>(ParentTablesOffset); - - for (int z = 0; z < ParentTables->size(); z++) + if (bOverrideIsComposite) { - auto ParentTable = ParentTables->At(z); + static auto ParentTablesOffset = ptr->GetOffset("ParentTables"); - if (ParentTable) + auto ParentTables = ptr->GetPtr>(ParentTablesOffset); + + for (int z = 0; z < ParentTables->size(); z++) { - LTDTables.push_back(ParentTable); + auto ParentTable = ParentTables->At(z); + + if (ParentTable) + { + LTDTables.push_back(ParentTable); + } } } - } - LTDTables.push_back(ptr); + LTDTables.push_back(ptr); + } } } } diff --git a/Project Reboot 3.0/FortPlayerController.cpp b/Project Reboot 3.0/FortPlayerController.cpp index af129fb..865ad28 100644 --- a/Project Reboot 3.0/FortPlayerController.cpp +++ b/Project Reboot 3.0/FortPlayerController.cpp @@ -40,7 +40,7 @@ void AFortPlayerController::ServerExecuteInventoryItemHook(AFortPlayerController if (Engine_Version <= 420) { static auto OverriddenBackpackSizeOffset = PlayerController->GetOffset("OverriddenBackpackSize"); - LOG_INFO(LogDev, "PlayerController->Get(OverriddenBackpackSizeOffset): {}", PlayerController->Get(OverriddenBackpackSizeOffset)); + // LOG_INFO(LogDev, "PlayerController->Get(OverriddenBackpackSizeOffset): {}", PlayerController->Get(OverriddenBackpackSizeOffset)); // PlayerController->Get(OverriddenBackpackSizeOffset) = 5; // PlayerController->ForceNetUpdate(); } @@ -220,7 +220,7 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame* auto Vehicle = ReceivingActor; ServerAttemptInteractOriginal(Context, Stack, Ret); - return; + // return; auto Pawn = (AFortPlayerPawn*)PlayerController->GetMyFortPawn(); @@ -287,19 +287,6 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame* return; } - /* - else if (ReceivingActor->IsA(FortAthenaSupplyDropClass)) - { - auto LootTierGroup = ReceivingActor->IsA(LlamaClass) ? UKismetStringLibrary::Conv_StringToName(L"Loot_AthenaLlama") : UKismetStringLibrary::Conv_StringToName(L"Loot_AthenaSupplyDrop"); // SupplyDrop->GetLootTierGroupOverride(); - - auto LootDrops = PickLootDrops(LootTierGroup); - - for (auto& LootDrop : LootDrops) - { - AFortPickup::SpawnPickup(LootDrop.ItemDefinition, LocationToSpawnLoot, LootDrop.Count, EFortPickupSourceTypeFlag::Other, EFortPickupSpawnSource::SupplyDrop); - } - } - */ return ServerAttemptInteractOriginal(Context, Stack, Ret); } diff --git a/Project Reboot 3.0/FortPlayerController.h b/Project Reboot 3.0/FortPlayerController.h index b781751..baf07d9 100644 --- a/Project Reboot 3.0/FortPlayerController.h +++ b/Project Reboot 3.0/FortPlayerController.h @@ -10,12 +10,29 @@ struct FFortAthenaLoadout { + static UStruct* GetStruct() + { + static auto Struct = FindObject("/Script/FortniteGame.FortAthenaLoadout"); + return Struct; + } + + static int GetStructSize() + { + return GetStruct()->GetPropertiesSize(); + } + UObject*& GetCharacter() { static auto CharacterOffset = FindOffsetStruct("/Script/FortniteGame.FortAthenaLoadout", "Character"); return *(UObject**)(__int64(this) + CharacterOffset); } + UObject*& GetBackpack() + { + static auto BackpackOffset = FindOffsetStruct("/Script/FortniteGame.FortAthenaLoadout", "Backpack"); + return *(UObject**)(__int64(this) + BackpackOffset); + } + UObject*& GetPickaxe() { static auto PickaxeOffset = FindOffsetStruct("/Script/FortniteGame.FortAthenaLoadout", "Pickaxe"); diff --git a/Project Reboot 3.0/FortPlayerControllerAthena.cpp b/Project Reboot 3.0/FortPlayerControllerAthena.cpp index bbd7284..a83edd3 100644 --- a/Project Reboot 3.0/FortPlayerControllerAthena.cpp +++ b/Project Reboot 3.0/FortPlayerControllerAthena.cpp @@ -72,12 +72,19 @@ void AFortPlayerControllerAthena::ServerAcknowledgePossessionHook(APlayerControl auto PawnAsFort = Cast(Pawn); auto PlayerStateAsFort = Cast(Pawn->GetPlayerState()); - if (Globals::bNoMCP) - return; - if (!PawnAsFort) return; + if (Globals::bNoMCP) + { + static auto CustomCharacterPartClass = FindObject("/Script/FortniteGame.CustomCharacterPart"); + static auto backpackPart = LoadObject("/Game/Characters/CharacterParts/Backpacks/NoBackpack.NoBackpack", CustomCharacterPartClass); + + // PawnAsFort->ServerChoosePart(EFortCustomPartType::Backpack, backpackPart); + + return; + } + static auto UpdatePlayerCustomCharacterPartsVisualizationFn = FindObject("/Script/FortniteGame.FortKismetLibrary.UpdatePlayerCustomCharacterPartsVisualization"); if (!UpdatePlayerCustomCharacterPartsVisualizationFn) @@ -85,8 +92,41 @@ void AFortPlayerControllerAthena::ServerAcknowledgePossessionHook(APlayerControl auto CosmeticLoadout = ControllerAsFort->GetCosmeticLoadout(); if (CosmeticLoadout) + { + /* static auto Pawn_CosmeticLoadoutOffset = PawnAsFort->GetOffset("CosmeticLoadout"); + + if (Pawn_CosmeticLoadoutOffset != -1) + { + CopyStruct(PawnAsFort->GetPtr<__int64>(Pawn_CosmeticLoadoutOffset), CosmeticLoadout, FFortAthenaLoadout::GetStructSize()); + } */ + ApplyCID(PawnAsFort, CosmeticLoadout->GetCharacter()); + auto Backpack = CosmeticLoadout->GetBackpack(); + + if (Backpack) + { + static auto CharacterPartsOffset = Backpack->GetOffset("CharacterParts"); + + if (CharacterPartsOffset != -1) + { + auto& BackpackCharacterParts = Backpack->Get>(CharacterPartsOffset); + + for (int i = 0; i < BackpackCharacterParts.Num(); i++) + { + auto BackpackCharacterPart = BackpackCharacterParts.at(i); + + if (!BackpackCharacterPart) + continue; + + PawnAsFort->ServerChoosePart(EFortCustomPartType::Backpack, BackpackCharacterPart); + } + + // UFortKismetLibrary::ApplyCharacterCosmetics(GetWorld(), BackpackCharacterParts, PlayerStateAsFort, &aa); + } + } + } + return; } diff --git a/Project Reboot 3.0/FortPlayerControllerAthena.h b/Project Reboot 3.0/FortPlayerControllerAthena.h index 1e424c5..0a001bb 100644 --- a/Project Reboot 3.0/FortPlayerControllerAthena.h +++ b/Project Reboot 3.0/FortPlayerControllerAthena.h @@ -26,7 +26,8 @@ static void ApplyCID(AFortPlayerPawn* Pawn, UObject* CID) { auto& SpecializationSoft = Specializations.at(i); - auto Specialization = SpecializationSoft.Get(); + static auto FortHeroSpecializationClass = FindObject("/Script/FortniteGame.FortHeroSpecialization"); + auto Specialization = SpecializationSoft.Get(FortHeroSpecializationClass, true); if (Specialization) { @@ -40,7 +41,8 @@ static void ApplyCID(AFortPlayerPawn* Pawn, UObject* CID) for (int z = 0; z < CharacterParts.Num(); z++) { auto& CharacterPartSoft = CharacterParts.at(z); - auto CharacterPart = CharacterPartSoft.Get(); + static auto CustomCharacterPartClass = FindObject("/Script/FortniteGame.CustomCharacterPart"); + auto CharacterPart = CharacterPartSoft.Get(CustomCharacterPartClass, true); CharacterPartsaa.Add(CharacterPart); diff --git a/Project Reboot 3.0/FortPlayerPawn.cpp b/Project Reboot 3.0/FortPlayerPawn.cpp index 77b2aa2..6eb86ab 100644 --- a/Project Reboot 3.0/FortPlayerPawn.cpp +++ b/Project Reboot 3.0/FortPlayerPawn.cpp @@ -14,7 +14,7 @@ void AFortPlayerPawn::ServerChoosePart(EFortCustomPartType Part, UObject* Chosen this->ProcessEvent(fn, &AFortPlayerPawn_ServerChoosePart_Params); } -void AFortPlayerPawn::ForceLaunchPlayerZiplinine() // Thanks android +void AFortPlayerPawn::ForceLaunchPlayerZipline() // Thanks android { float ZiplineJumpDampening = -0.5f; float ZiplineJumpStrength = 1500.f; @@ -70,7 +70,7 @@ void AFortPlayerPawn::ServerSendZiplineStateHook(AFortPlayerPawn* Pawn, FZipline { if ((*(bool*)(__int64(PawnZiplineState) + bJumpedOffset))) { - Pawn->ForceLaunchPlayerZiplinine(); + Pawn->ForceLaunchPlayerZipline(); } } } diff --git a/Project Reboot 3.0/FortPlayerPawn.h b/Project Reboot 3.0/FortPlayerPawn.h index b4efb95..c6a807f 100644 --- a/Project Reboot 3.0/FortPlayerPawn.h +++ b/Project Reboot 3.0/FortPlayerPawn.h @@ -23,7 +23,7 @@ class AFortPlayerPawn : public AFortPawn { public: void ServerChoosePart(EFortCustomPartType Part, class UObject* ChosenCharacterPart); - void ForceLaunchPlayerZiplinine(); // Thanks android + void ForceLaunchPlayerZipline(); // Thanks android static void ServerSendZiplineStateHook(AFortPlayerPawn* Pawn, FZiplinePawnState InZiplineState); static void ServerHandlePickupHook(AFortPlayerPawn* Pawn, AFortPickup* Pickup, float InFlyTime, FVector InStartDirection, bool bPlayPickupSound); diff --git a/Project Reboot 3.0/Level.cpp b/Project Reboot 3.0/Level.cpp new file mode 100644 index 0000000..d75de64 --- /dev/null +++ b/Project Reboot 3.0/Level.cpp @@ -0,0 +1,25 @@ +#include "Level.h" + +UWorld*& ULevel::GetOwningWorld() +{ + static auto OwningWorldOffset = GetOffset("OwningWorld"); + return Get(OwningWorldOffset); +} + +bool ULevel::HasVisibilityChangeRequestPending() +{ + // I believe implementation on this changes depending on the version + + auto OwningWorld = GetOwningWorld(); + + if (!OwningWorld) + return false; + + static auto CurrentLevelPendingVisibilityOffset = OwningWorld->GetOffset("CurrentLevelPendingVisibility"); + auto CurrentLevelPendingVisibility = OwningWorld->Get(CurrentLevelPendingVisibilityOffset); + + static auto CurrentLevelPendingInvisibilityOffset= OwningWorld->GetOffset("CurrentLevelPendingInvisibility"); + auto CurrentLevelPendingInvisibility = OwningWorld->Get(CurrentLevelPendingInvisibilityOffset); + + return this == CurrentLevelPendingVisibility || this == CurrentLevelPendingInvisibility; +} \ No newline at end of file diff --git a/Project Reboot 3.0/Level.h b/Project Reboot 3.0/Level.h new file mode 100644 index 0000000..314da12 --- /dev/null +++ b/Project Reboot 3.0/Level.h @@ -0,0 +1,10 @@ +#pragma once + +#include "World.h" + +class ULevel : public UObject +{ +public: + UWorld*& GetOwningWorld(); + bool HasVisibilityChangeRequestPending(); +}; \ No newline at end of file diff --git a/Project Reboot 3.0/NetDriver.cpp b/Project Reboot 3.0/NetDriver.cpp index 523c39a..46c4dd9 100644 --- a/Project Reboot 3.0/NetDriver.cpp +++ b/Project Reboot 3.0/NetDriver.cpp @@ -5,6 +5,20 @@ #include "NetConnection.h" #include "FortPlayerControllerAthena.h" #include "GameplayStatics.h" +#include "KismetMathLibrary.h" +#include + +FNetworkObjectList& UNetDriver::GetNetworkObjectList() +{ + return *(*(TSharedPtr*)(__int64(this) + 0x490)); +} + +void UNetDriver::RemoveNetworkActor(AActor* Actor) +{ + GetNetworkObjectList().Remove(Actor); + + // RenamedStartupActors.Remove(Actor->GetFName()); +} void UNetDriver::TickFlushHook(UNetDriver* NetDriver) { @@ -34,7 +48,7 @@ int32 ServerReplicateActors_PrepConnections(UNetDriver* NetDriver) for (int32 ConnIdx = 0; ConnIdx < ClientConnections.Num(); ConnIdx++) { UNetConnection* Connection = ClientConnections.at(ConnIdx); - // check(Connection); + if (!Connection) continue; // check(Connection->State == USOCK_Pending || Connection->State == USOCK_Open || Connection->State == USOCK_Closed); // checkSlow(Connection->GetUChildConnection() == NULL); @@ -42,13 +56,8 @@ int32 ServerReplicateActors_PrepConnections(UNetDriver* NetDriver) if (OwningActor != NULL) // && /* Connection->State == USOCK_Open && */ (Connection->Driver->Time - Connection->LastReceiveTime < 1.5f)) { - // check(World == OwningActor->GetWorld()); - bFoundReadyConnection = true; - // the view target is what the player controller is looking at OR the owning actor itself when using beacons - // Connection->GetViewTarget() = Connection->GetPlayerController() ? Connection->GetPlayerController()->GetViewTarget() : OwningActor; - AActor* DesiredViewTarget = OwningActor; if (Connection->GetPlayerController()) @@ -90,42 +99,61 @@ enum class ENetDormancy : uint8_t ENetDormancy_MAX = 6 }; -struct FNetworkObjectInfo +FORCEINLINE float FRand() { - AActor* Actor; - /* TWeakObjectPtr WeakActor; - double NextUpdateTime; - double LastNetReplicateTime; - float OptimalNetUpdateDelta; - float LastNetUpdateTime; - uint32 bPendingNetUpdate : 1; - uint32 bForceRelevantNextUpdate : 1; - TSet> DormantConnections; - TSet> RecentlyDormantConnections; */ -}; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> dis(0, 1); + float random_number = dis(gen); -static void ServerReplicateActors_BuildConsiderList(UNetDriver* NetDriver, std::vector& OutConsiderList) + return random_number; +} + +#define USEOBJECTLIST + +void UNetDriver::ServerReplicateActors_BuildConsiderList(std::vector& OutConsiderList) { + std::vector ActorsToRemove; + +#ifdef USEOBJECTLIST + auto& ActiveObjects = GetNetworkObjectList().ActiveNetworkObjects; +#else TArray Actors = UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass()); +#endif - /* auto& ActiveObjects = GetNetworkObjectList(NetDriver).ActiveNetworkObjects; + auto World = GetWorld(); - for (int i = 0; i < ActiveObjects.Num(); i++) +#ifdef USEOBJECTLIST + // for (int i = 0; i < ActiveObjects.Elements.Num(); i++) + for (const TSharedPtr& ActorInfo : ActiveObjects) { - auto ActorInfo = ActiveObjects.GetElements().GetData()[i].ElementData.Value.Get(); - auto Actor = ActorInfo->Actor; */ + // auto& ActorInfo = ActiveObjects.Elements.Data.at(i).ElementData.Value; + + if (!ActorInfo->bPendingNetUpdate && UGameplayStatics::GetTimeSeconds(GetWorld()) <= ActorInfo->NextUpdateTime) + { + continue; + } + + // if (IsBadReadPtr(ActorInfo, 8)) + // continue; + + auto Actor = ActorInfo->Actor; + +#else for (int i = 0; i < Actors.Num(); i++) { auto Actor = Actors.at(i); +#endif + if (!Actor) continue; - // if (!Actor->bActorInitialized) continue; - - if (Actor->IsActorBeingDestroyed()) + if (Actor->IsPendingKillPending()) + // if (Actor->IsPendingKill()) { + ActorsToRemove.push_back(Actor); continue; } @@ -133,25 +161,93 @@ static void ServerReplicateActors_BuildConsiderList(UNetDriver* NetDriver, std:: 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->IsNetStartup()) + if (Actor->Get(NetDormancyOffset) == ENetDormancy::DORM_Initial && Actor->IsNetStartupActor()) // IsDormInitialStartupActor { continue; } - static void (*CallPreReplication)(AActor*, UNetDriver*) = decltype(CallPreReplication)(Addresses::CallPreReplication); - CallPreReplication(Actor, NetDriver); + // 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. +#ifndef USEOBJECTLIST FNetworkObjectInfo* ActorInfo = new FNetworkObjectInfo; ActorInfo->Actor = Actor; - OutConsiderList.push_back(ActorInfo); +#else + auto TimeSeconds = UGameplayStatics::GetTimeSeconds(World); // Can we do this outside of the loop? + + 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 = TimeSeconds - 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->GetNetUpdateFrequency(), 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 = TimeSeconds + FRand() * ServerTickTime + NextUpdateDelta; + static auto TimeOffset = GetOffset("Time"); + ActorInfo->LastNetUpdateTime = Get(TimeOffset); + } + + ActorInfo->bPendingNetUpdate = false; +#endif + + OutConsiderList.push_back(ActorInfo.Get()); + + static void (*CallPreReplication)(AActor*, UNetDriver*) = decltype(CallPreReplication)(Addresses::CallPreReplication); + CallPreReplication(Actor, this); } +#ifndef USEOBJECTLIST Actors.Free(); +#else + for (auto Actor : ActorsToRemove) + { + if (!Actor) + continue; + + /* LOG_INFO(LogDev, "Removing actor: {}", Actor ? Actor->GetFullName() : "InvalidObject"); + RemoveNetworkActor(Actor); + LOG_INFO(LogDev, "Finished removing actor."); */ + } +#endif } using UChannel = UObject; @@ -272,16 +368,21 @@ int32 UNetDriver::ServerReplicateActors() } else */ { - ServerTickTime = 1.f / ServerTickTime; // 0 + ServerTickTime = 1.f / ServerTickTime; // bCPUSaturated = DeltaSeconds > 1.2f * ServerTickTime; } std::vector ConsiderList; - // ConsiderList.reserve(GetNetworkObjectList(NetDriver).ActiveNetworkObjects.Num()); + +#ifdef USEOBJECTLIST + ConsiderList.reserve(GetNetworkObjectList().ActiveNetworkObjects.Num()); +#endif // std::cout << "ConsiderList.size(): " << GetNetworkObjectList(NetDriver).ActiveNetworkObjects.Num() << '\n'; - ServerReplicateActors_BuildConsiderList(this, ConsiderList); + auto World = GetWorld(); + + ServerReplicateActors_BuildConsiderList(ConsiderList); for (int32 i = 0; i < this->GetClientConnections().Num(); i++) { @@ -347,15 +448,35 @@ int32 UNetDriver::ServerReplicateActors() Channel = (UActorChannel*)CreateChannel(Connection, 2, true, -1); - if (Channel) { + if (Channel) + { SetChannelActor(Channel, Actor); - // Channel->Connection = Connection; } +#ifdef USEOBJECTLIST + if (Actor->GetNetUpdateFrequency() < 1.0f) + { + ActorInfo->NextUpdateTime = UGameplayStatics::GetTimeSeconds(GetWorld()) + 0.2f * FRand(); + } +#endif } if (Channel) - ReplicateActor(Channel); + { + if (ReplicateActor(Channel)) + { +#ifdef USEOBJECTLIST + 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; +#endif + } + } } } diff --git a/Project Reboot 3.0/NetDriver.h b/Project Reboot 3.0/NetDriver.h index a81e9ae..59e4e14 100644 --- a/Project Reboot 3.0/NetDriver.h +++ b/Project Reboot 3.0/NetDriver.h @@ -7,6 +7,71 @@ #include "NetConnection.h" #include "Array.h" +#include "WeakObjectPtrTemplates.h" +#include "Map.h" +#include "SharedPointer.h" +#include "Level.h" + +struct FActorDestructionInfo +{ + TWeakObjectPtr Level; + TWeakObjectPtr ObjOuter; + FVector DestroyedPosition; + int32 NetGUID; + FString PathName; + FName StreamingLevelName; +}; +struct FNetworkObjectInfo +{ + /** Pointer to the replicated actor. */ + AActor* Actor; + + /** WeakPtr to actor. This is cached here to prevent constantly constructing one when needed for (things like) keys in TMaps/TSets */ + TWeakObjectPtr WeakActor; + + /** Next time to consider replicating the actor. Based on FPlatformTime::Seconds(). */ + double NextUpdateTime; + + /** Last absolute time in seconds since actor actually sent something during replication */ + double LastNetReplicateTime; + + /** Optimal delta between replication updates based on how frequently actor properties are actually changing */ + float OptimalNetUpdateDelta; + + /** Last time this actor was updated for replication via NextUpdateTime + * @warning: internal net driver time, not related to WorldSettings.TimeSeconds */ + float LastNetUpdateTime; + + /** Is this object still pending a full net update due to clients that weren't able to replicate the actor at the time of LastNetUpdateTime */ + uint32 bPendingNetUpdate : 1; + + /** Force this object to be considered relevant for at least one update */ + uint32 bForceRelevantNextUpdate : 1; + + /** List of connections that this actor is dormant on */ + TSet> DormantConnections; + + /** A list of connections that this actor has recently been dormant on, but the actor doesn't have a channel open yet. + * These need to be differentiated from actors that the client doesn't know about, but there's no explicit list for just those actors. + * (this list will be very transient, with connections being moved off the DormantConnections list, onto this list, and then off once the actor has a channel again) + */ + TSet> RecentlyDormantConnections; +}; + +class FNetworkObjectList +{ +public: + typedef TSet> FNetworkObjectSet; + + FNetworkObjectSet AllNetworkObjects; + FNetworkObjectSet ActiveNetworkObjects; + FNetworkObjectSet ObjectsDormantOnAllConnections; + + TMap, int32> NumDormantObjectsPerConnection; + + void Remove(AActor* const Actor); +}; + class UWorld; struct FURL // idk where this actually goes @@ -37,7 +102,10 @@ public: return Get>(ClientConnectionsOffset); } + void RemoveNetworkActor(AActor* Actor); 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(); + void ServerReplicateActors_BuildConsiderList(std::vector& OutConsiderList); + FNetworkObjectList& GetNetworkObjectList(); }; \ No newline at end of file diff --git a/Project Reboot 3.0/NetworkObjectList.cpp b/Project Reboot 3.0/NetworkObjectList.cpp new file mode 100644 index 0000000..92e4b39 --- /dev/null +++ b/Project Reboot 3.0/NetworkObjectList.cpp @@ -0,0 +1,107 @@ +#include "NetDriver.h" + +void FNetworkObjectList::Remove(AActor* const Actor) +{ + if (Actor == nullptr) + { + return; + } + + TSharedPtr* NetworkObjectInfoPtr = nullptr; + + for (int i = 0; i < AllNetworkObjects.Num(); i++) + { + auto& CurrentNetworkObject = AllNetworkObjects[i]; + + if (CurrentNetworkObject->Actor == Actor) + { + NetworkObjectInfoPtr = &CurrentNetworkObject; + break; + } + } + + if (NetworkObjectInfoPtr == nullptr) + { + // Sanity check that we're not on the other lists either + // check(!ActiveNetworkObjects.Contains(Actor)); + // check(!ObjectsDormantOnAllConnections.Contains(Actor)); + // check((ActiveNetworkObjects.Num() + ObjectsDormantOnAllConnections.Num()) == AllNetworkObjects.Num()); + return; + } + + FNetworkObjectInfo* NetworkObjectInfo = NetworkObjectInfoPtr->Get(); + + for (int i = 0; i < NetworkObjectInfo->DormantConnections.Num(); i++) + { + auto& ConnectionIt = NetworkObjectInfo->DormantConnections[i]; + + UNetConnection* Connection = ConnectionIt.Get(); + + if (Connection == nullptr) // || Connection->State == USOCK_Closed) + { + NetworkObjectInfo->DormantConnections.Remove(i); + // ConnectionIt.RemoveCurrent(); + continue; + } + + int32* NumDormantObjectsPerConnectionRef = nullptr; + + for (int z = 0; z < NumDormantObjectsPerConnection.Pairs.Num(); z++) + { + auto& Pair = NumDormantObjectsPerConnection.Pairs[z]; + + if (Pair.First.ObjectIndex == Connection->InternalIndex) + { + NumDormantObjectsPerConnectionRef = &Pair.Second; + break; + } + } + + if (!NumDormantObjectsPerConnectionRef) + { + // We should add here TODO MILXNOR + } + + // check(NumDormantObjectsPerConnectionRef > 0); + + if (NumDormantObjectsPerConnectionRef) + (*NumDormantObjectsPerConnectionRef)--; + } + + // Remove this object from all lists + + for (int i = 0; i < AllNetworkObjects.Num(); i++) + { + auto& CurrentNetworkObject = AllNetworkObjects[i]; + + if (CurrentNetworkObject->Actor == Actor) + { + AllNetworkObjects.Remove(i); + break; + } + } + + for (int i = 0; i < ActiveNetworkObjects.Num(); i++) + { + auto& CurrentActiveNetworkObject = ActiveNetworkObjects[i]; + + if (CurrentActiveNetworkObject->Actor == Actor) + { + ActiveNetworkObjects.Remove(i); + break; + } + } + + for (int i = 0; i < ObjectsDormantOnAllConnections.Num(); i++) + { + auto& CurrentDormantObject = ObjectsDormantOnAllConnections[i]; + + if (CurrentDormantObject->Actor == Actor) + { + ObjectsDormantOnAllConnections.Remove(i); + break; + } + } + + // check((ActiveNetworkObjects.Num() + ObjectsDormantOnAllConnections.Num()) == AllNetworkObjects.Num()); +} \ No newline at end of file diff --git a/Project Reboot 3.0/Object.cpp b/Project Reboot 3.0/Object.cpp index 21d90c6..5832734 100644 --- a/Project Reboot 3.0/Object.cpp +++ b/Project Reboot 3.0/Object.cpp @@ -132,4 +132,9 @@ bool UObject::IsValidLowLevel() return false; } return ChunkedObjects ? ChunkedObjects->IsValid(this) : UnchunkedObjects ? UnchunkedObjects->IsValid(this) : false; +} + +FORCEINLINE bool UObject::IsPendingKill() const +{ + return ChunkedObjects ? ChunkedObjects->GetItemByIndex(InternalIndex)->IsPendingKill() : UnchunkedObjects ? UnchunkedObjects->GetItemByIndex(InternalIndex)->IsPendingKill() : false; } \ No newline at end of file diff --git a/Project Reboot 3.0/Object.h b/Project Reboot 3.0/Object.h index a7cf0cd..ca1419f 100644 --- a/Project Reboot 3.0/Object.h +++ b/Project Reboot 3.0/Object.h @@ -90,6 +90,25 @@ public: void AddToRoot(); bool IsValidLowLevel(); + FORCEINLINE bool IsPendingKill() const; // static class UClass* StaticClass(); -}; \ No newline at end of file +}; + +/* struct FInternalUObjectBaseUtilityIsValidFlagsChecker +{ + FORCEINLINE static bool CheckObjectValidBasedOnItsFlags(const UObject* Test) + { + // Here we don't really check if the flags match but if the end result is the same + checkSlow(GUObjectArray.IndexToObject(Test->InternalIndex)->HasAnyFlags(EInternalObjectFlags::PendingKill | EInternalObjectFlags::Garbage) == Test->HasAnyFlags(RF_InternalPendingKill | RF_InternalGarbage)); + return !Test->HasAnyFlags(RF_InternalPendingKill | RF_InternalGarbage); + } +}; */ + +FORCEINLINE bool IsValidChecked(const UObject* Test) +{ + // if (!Test) + // return false; + + return true; // FInternalUObjectBaseUtilityIsValidFlagsChecker::CheckObjectValidBasedOnItsFlags(Test); +} \ No newline at end of file diff --git a/Project Reboot 3.0/ObjectMacros.h b/Project Reboot 3.0/ObjectMacros.h index eeea069..3cd4a23 100644 --- a/Project Reboot 3.0/ObjectMacros.h +++ b/Project Reboot 3.0/ObjectMacros.h @@ -50,6 +50,7 @@ enum class EInternalObjectFlags : int AsyncLoading = 1 << 27, ///< Object is being asynchronously loaded. Unreachable = 1 << 28, ///< Object is not reachable on the object graph. // PendingKill UE_DEPRECATED(5.0, "PendingKill flag should no longer be used. Use Garbage flag instead.") = 1 << 29, ///< Objects that are pending destruction (invalid for gameplay but valid objects). This flag is mirrored in EObjectFlags as RF_PendingKill for performance + PendingKill = 1 << 29, RootSet = 1 << 30, ///< Object will not be garbage collected, even if unreferenced. PendingConstruction = 1 << 31 ///< Object didn't have its class constructor called yet (only the UObjectBase one to initialize its most basic members) diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj b/Project Reboot 3.0/Project Reboot 3.0.vcxproj index f0d72c8..467f334 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj @@ -209,10 +209,12 @@ + + @@ -295,6 +297,7 @@ + @@ -315,6 +318,7 @@ + @@ -328,6 +332,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 aa92e87..ea2cbe4 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters @@ -167,6 +167,12 @@ FortniteGame\Source\FortniteGame\Private\Player + + Engine\Source\Runtime\Engine\Private + + + Engine\Source\Runtime\Engine\Private + @@ -482,6 +488,15 @@ FortniteGame\Source\FortniteGame\Public\Items + + Engine\Source\Runtime\CoreUObject\Public\UObject + + + Engine\Source\Runtime\Core\Public\Templates + + + Engine\Source\Runtime\Engine\Classes\Engine + @@ -628,9 +643,6 @@ {247d4c62-23f7-4964-8879-5a0d65c44a73} - - {31a7f342-8b7c-4594-a24d-c4dd5c9d230d} - {bcb0d983-0b85-4ca6-9fac-6567c7d79921} @@ -667,6 +679,9 @@ {915f7622-6003-445e-a43f-04d5c7e13b49} + + {31a7f342-8b7c-4594-a24d-c4dd5c9d230d} + diff --git a/Project Reboot 3.0/Set.h b/Project Reboot 3.0/Set.h index e51807a..adc03f6 100644 --- a/Project Reboot 3.0/Set.h +++ b/Project Reboot 3.0/Set.h @@ -32,7 +32,173 @@ public: } }; -template //, typename KeyFuncs, typename Allocator> +template +class TSet +{ +private: + friend TSparseArray; + +public: + typedef TSetElement ElementType; + typedef TSparseArrayElementOrListLink ArrayElementType; + +public: + TSparseArray Elements; + + mutable TInlineAllocator<1>::ForElementType Hash; + mutable int32 HashSize; + +public: + class FBaseIterator + { + private: + TSet& IteratedSet; + TSparseArray::FBaseIterator ElementIt; + + public: + FORCEINLINE FBaseIterator(const TSet& InSet, TSparseArray>::FBaseIterator InElementIt) + : IteratedSet(const_cast&>(InSet)) + , ElementIt(InElementIt) + { + } + + FORCEINLINE explicit operator bool() const + { + return (bool)ElementIt; + } + FORCEINLINE TSet::FBaseIterator& operator++() + { + ++ElementIt; + return *this; + } + FORCEINLINE bool operator==(const TSet::FBaseIterator& OtherIt) const + { + return ElementIt == OtherIt.ElementIt; + } + FORCEINLINE bool operator!=(const TSet::FBaseIterator& OtherIt) const + { + return ElementIt != OtherIt.ElementIt; + } + FORCEINLINE TSet::FBaseIterator& operator=(TSet::FBaseIterator& OtherIt) + { + return ElementIt = OtherIt.ElementIt; + } + FORCEINLINE SetType& operator*() + { + return (*ElementIt).Value; + } + FORCEINLINE const SetType& operator*() const + { + return &((*ElementIt).Value); + } + FORCEINLINE SetType* operator->() + { + return &((*ElementIt).Value); + } + FORCEINLINE const SetType* operator->() const + { + return &(*ElementIt).Value; + } + FORCEINLINE const int32 GetIndex() const + { + return ElementIt.GetIndex(); + } + FORCEINLINE ElementType& GetSetElement() + { + return *ElementIt; + } + FORCEINLINE const ElementType& GetSetElement() const + { + return *ElementIt; + } + FORCEINLINE bool IsElementValid() const + { + return ElementIt.IsElementValid(); + } + }; + +public: + FORCEINLINE TSet::FBaseIterator begin() + { + return TSet::FBaseIterator(*this, Elements.begin()); + } + FORCEINLINE const TSet::FBaseIterator begin() const + { + 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()); + } + + FORCEINLINE SetType& operator[](int Index) + { + return Elements[Index].ElementData.Value; + } + + FORCEINLINE int32 Num() const + { + return Elements.Num(); + } + FORCEINLINE bool IsValid() const + { + return Elements.Data.Data != nullptr && Elements.AllocationFlags.MaxBits > 0; + } + FORCEINLINE TSparseArray& GetElements() + { + return Elements; + } + FORCEINLINE const TSparseArray& GetElements() const + { + 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 + { + if (Num() <= 0) + return false; + + for (SetType Element : *this) + { + 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) + { + return It.GetIndex(); + } + } + return -1; + } + FORCEINLINE bool Remove(const SetType& ElementToRemove) + { + return Elements.RemoveAt(Find(ElementToRemove)); + } + FORCEINLINE bool Remove(int Index) + { + return Elements.RemoveAt(Index); + } +}; + + +/* template //, typename KeyFuncs, typename Allocator> class TSet { public: @@ -43,4 +209,4 @@ public: mutable TInlineAllocator<1>::ForElementType Hash; mutable int32 HashSize; -}; \ No newline at end of file +}; */ \ No newline at end of file diff --git a/Project Reboot 3.0/SharedPointer.h b/Project Reboot 3.0/SharedPointer.h new file mode 100644 index 0000000..d9a2b0a --- /dev/null +++ b/Project Reboot 3.0/SharedPointer.h @@ -0,0 +1,36 @@ +#pragma once + +template< class ObjectType> +class TSharedPtr +{ +public: + ObjectType* Object; + + int32 SharedReferenceCount; + int32 WeakReferenceCount; + + FORCEINLINE ObjectType* Get() + { + return Object; + } + FORCEINLINE ObjectType* Get() const + { + return Object; + } + FORCEINLINE ObjectType& operator*() + { + return *Object; + } + FORCEINLINE const ObjectType& operator*() const + { + return *Object; + } + FORCEINLINE ObjectType* operator->() + { + return Object; + } + FORCEINLINE ObjectType* operator->() const + { + return Object; + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/SparseArray.h b/Project Reboot 3.0/SparseArray.h index 5f52d3d..3e9302d 100644 --- a/Project Reboot 3.0/SparseArray.h +++ b/Project Reboot 3.0/SparseArray.h @@ -50,6 +50,86 @@ public: int32 FirstFreeIndex; int32 NumFreeIndices; + FORCEINLINE int32 Num() const + { + return Data.Num() - NumFreeIndices; + } + + class FBaseIterator + { + private: + TSparseArray& IteratedArray; + TBitArray::FSetBitIterator BitArrayIt; + + public: + FORCEINLINE FBaseIterator(const TSparseArray& Array, const TBitArray::FSetBitIterator BitIterator) + : IteratedArray(const_cast&>(Array)) + , BitArrayIt(const_cast(BitIterator)) + { + } + + FORCEINLINE explicit operator bool() const + { + return (bool)BitArrayIt; + } + FORCEINLINE TSparseArray::FBaseIterator& operator++() + { + ++BitArrayIt; + return *this; + } + FORCEINLINE ArrayType& operator*() + { + 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; + } + + FORCEINLINE int32 GetIndex() const + { + return BitArrayIt.GetIndex(); + } + FORCEINLINE bool IsElementValid() const + { + return *BitArrayIt; + } + }; + +public: + FORCEINLINE TSparseArray::FBaseIterator begin() + { + return TSparseArray::FBaseIterator(*this, TBitArray::FSetBitIterator(AllocationFlags, 0)); + } + FORCEINLINE const TSparseArray::FBaseIterator begin() 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)); + } + FORCEINLINE FSparseArrayElement& operator[](uint32 Index) { return *(FSparseArrayElement*)&Data.at(Index).ElementData; @@ -58,9 +138,62 @@ public: { return *(const FSparseArrayElement*)&Data.at(Index).ElementData; } - - FORCEINLINE int32 Num() const + FORCEINLINE int32 GetNumFreeIndices() const { - return Data.Num() - NumFreeIndices; + return NumFreeIndices; + } + FORCEINLINE int32 GetFirstFreeIndex() const + { + return FirstFreeIndex; + } + FORCEINLINE const TArray& GetData() const + { + return Data; + } + FORCEINLINE const TBitArray& GetAllocationFlags() const + { + return AllocationFlags; + } + FORCEINLINE bool IsIndexValid(int32 IndexToCheck) const + { + return AllocationFlags.IsSet(IndexToCheck); + } + + FORCEINLINE bool RemoveAt(const int32 IndexToRemove) + { + if (IndexToRemove >= 0 && IndexToRemove < Data.Num() && AllocationFlags.IsSet(IndexToRemove)) + { + int32 PreviousFreeIndex = -1; + int32 NextFreeIndex = -1; + + 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/UObjectArray.h b/Project Reboot 3.0/UObjectArray.h index 5640625..15d9f78 100644 --- a/Project Reboot 3.0/UObjectArray.h +++ b/Project Reboot 3.0/UObjectArray.h @@ -12,6 +12,11 @@ struct FUObjectItem int32 ClusterRootIndex; int32 SerialNumber; + FORCEINLINE bool IsPendingKill() const + { + return !!(Flags & int32(EInternalObjectFlags::PendingKill)); + } + FORCEINLINE void SetFlag(EInternalObjectFlags FlagToSet) { // static_assert(sizeof(int32) == sizeof(Flags), "Flags must be 32-bit for atomics."); diff --git a/Project Reboot 3.0/WeakObjectPtr.h b/Project Reboot 3.0/WeakObjectPtr.h index c4123b8..66dd808 100644 --- a/Project Reboot 3.0/WeakObjectPtr.h +++ b/Project Reboot 3.0/WeakObjectPtr.h @@ -1,8 +1,15 @@ #pragma once +#include "UObjectArray.h" + struct FWeakObjectPtr { public: int ObjectIndex; int ObjectSerialNumber; + + UObject* Get() + { + return ChunkedObjects ? ChunkedObjects->GetObjectByIndex(ObjectIndex) : UnchunkedObjects ? UnchunkedObjects->GetObjectByIndex(ObjectIndex) : nullptr; + } }; \ No newline at end of file diff --git a/Project Reboot 3.0/WeakObjectPtrTemplates.h b/Project Reboot 3.0/WeakObjectPtrTemplates.h new file mode 100644 index 0000000..0e32554 --- /dev/null +++ b/Project Reboot 3.0/WeakObjectPtrTemplates.h @@ -0,0 +1,16 @@ +#pragma once + +#include "WeakObjectPtr.h" +#include "Object.h" + +template +struct TWeakObjectPtr; + +template +struct TWeakObjectPtr : public TWeakObjectPtrBase +{ + T* Get() + { + return (T*)TWeakObjectPtrBase::Get(); + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/addresses.cpp b/Project Reboot 3.0/addresses.cpp index a15033a..62d15ce 100644 --- a/Project Reboot 3.0/addresses.cpp +++ b/Project Reboot 3.0/addresses.cpp @@ -395,7 +395,7 @@ std::vector Addresses::GetFunctionsToNull() if (Fortnite_Version > 2.5 && Engine_Version == 420) { toNull.push_back(Memcury::Scanner::FindPattern("48 8B C4 57 48 81 EC ? ? ? ? 4C 8B 82 ? ? ? ? 48 8B F9 0F 29 70 E8 0F 29 78 D8").Get()); // Pawn Overlap - toNull.push_back(Memcury::Scanner::FindPattern("E8 ? ? ? ? EB 26 40 38 3D ? ? ? ?").RelativeOffset(1).Get()); // collectgarbage + // toNull.push_back(Memcury::Scanner::FindPattern("E8 ? ? ? ? EB 26 40 38 3D ? ? ? ?").RelativeOffset(1).Get()); // collectgarbage } if (Engine_Version == 421) diff --git a/Project Reboot 3.0/dllmain.cpp b/Project Reboot 3.0/dllmain.cpp index 5b11862..efcfc0e 100644 --- a/Project Reboot 3.0/dllmain.cpp +++ b/Project Reboot 3.0/dllmain.cpp @@ -61,6 +61,34 @@ static __int64 DispatchRequestHook(__int64 a1, __int64* a2, int a3) return DispatchRequestOriginal(a1, a2, 3); } +void (*ApplyHomebaseEffectsOnPlayerSetupOriginal)( + __int64* GameState, + __int64 a2, + __int64 a3, + __int64 a4, + UObject* Hero, + char a6, + unsigned __int8 a7); + +void __fastcall ApplyHomebaseEffectsOnPlayerSetupHook( + __int64* GameState, + __int64 a2, + __int64 a3, + __int64 a4, + UObject* Hero, + char a6, + unsigned __int8 a7) +{ + LOG_INFO(LogDev, "Old hero: {}", Hero ? Hero->GetFullName() : "InvalidObject"); + + auto HeroType = FindObject("/Game/Athena/Heroes/HID_030_Athena_Commando_M_Halloween.HID_030_Athena_Commando_M_Halloween"); + + static auto ItemDefinitionOffset = Hero->GetOffset("ItemDefinition"); + Hero->Get(ItemDefinitionOffset) = HeroType; + + return ApplyHomebaseEffectsOnPlayerSetupOriginal(GameState, a2, a3, a4, Hero, a6, a7); +} + DWORD WINAPI Main(LPVOID) { InitLogger(); @@ -213,6 +241,8 @@ DWORD WINAPI Main(LPVOID) if (func == 0) continue; + LOG_INFO(LogDev, "Nulling 0x{:x}", func - __int64(GetModuleHandleW(0))); + DWORD dwProtection; VirtualProtect((PVOID)func, 1, PAGE_EXECUTE_READWRITE, &dwProtection); @@ -228,11 +258,16 @@ DWORD WINAPI Main(LPVOID) // Globals::bAbilitiesEnabled = Engine_Version < 500; + if (Fortnite_Version == 1.11) + { + auto ApplyHomebaseEffectsOnPlayerSetupAddr = Memcury::Scanner::FindPattern("40 55 53 57 41 54 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 00 4C 8B BD ? ? ? ? 49").Get(); + + Hooking::MinHook::Hook((PVOID)ApplyHomebaseEffectsOnPlayerSetupAddr, ApplyHomebaseEffectsOnPlayerSetupHook, (PVOID*)&ApplyHomebaseEffectsOnPlayerSetupOriginal); + } + Hooking::MinHook::Hook(GameModeDefault, FindObject(L"/Script/Engine.GameMode.ReadyToStartMatch"), AFortGameModeAthena::Athena_ReadyToStartMatchHook, (PVOID*)&AFortGameModeAthena::Athena_ReadyToStartMatchOriginal, false); - // return false; - Hooking::MinHook::Hook(GameModeDefault, FindObject(L"/Script/Engine.GameModeBase.SpawnDefaultPawnFor"), AGameModeBase::SpawnDefaultPawnForHook, nullptr, false); Hooking::MinHook::Hook(GameModeDefault, FindObject(L"/Script/Engine.GameModeBase.HandleStartingNewPlayer"), AFortGameModeAthena::Athena_HandleStartingNewPlayerHook, @@ -576,6 +611,56 @@ DWORD WINAPI Main(LPVOID) else if (GetAsyncKeyState(VK_F8) & 1) { + auto GameMode = (AFortGameMode*)GetWorld()->GetGameMode(); + auto GameState = GameMode->GetGameState(); + + if (Fortnite_Version == 1.11) + { + static auto OverrideBattleBusSkin = FindObject("/Game/Athena/Items/Cosmetics/BattleBuses/BBID_WinterBus.BBID_WinterBus"); + LOG_INFO(LogDev, "OverrideBattleBusSkin: {}", __int64(OverrideBattleBusSkin)); + + if (OverrideBattleBusSkin) + { + static auto AssetManagerOffset = GetEngine()->GetOffset("AssetManager"); + auto AssetManager = GetEngine()->Get(AssetManagerOffset); + + if (AssetManager) + { + static auto AthenaGameDataOffset = AssetManager->GetOffset("AthenaGameData"); + auto AthenaGameData = AssetManager->Get(AthenaGameDataOffset); + + if (AthenaGameData) + { + static auto DefaultBattleBusSkinOffset = AthenaGameData->GetOffset("DefaultBattleBusSkin"); + AthenaGameData->Get(DefaultBattleBusSkinOffset) = OverrideBattleBusSkin; + } + } + + static auto DefaultBattleBusOffset = GameState->GetOffset("DefaultBattleBus"); + GameState->Get(DefaultBattleBusOffset) = OverrideBattleBusSkin; + + static auto FortAthenaAircraftClass = FindObject("/Script/FortniteGame.FortAthenaAircraft"); + auto AllAircrafts = UGameplayStatics::GetAllActorsOfClass(GetWorld(), FortAthenaAircraftClass); + + for (int i = 0; i < AllAircrafts.Num(); i++) + { + auto Aircraft = AllAircrafts.at(i); + + static auto DefaultBusSkinOffset = Aircraft->GetOffset("DefaultBusSkin"); + Aircraft->Get(DefaultBusSkinOffset) = OverrideBattleBusSkin; + + static auto SpawnedCosmeticActorOffset = Aircraft->GetOffset("SpawnedCosmeticActor"); + auto SpawnedCosmeticActor = Aircraft->Get(SpawnedCosmeticActorOffset); + + if (SpawnedCosmeticActor) + { + static auto ActiveSkinOffset = SpawnedCosmeticActor->GetOffset("ActiveSkin"); + SpawnedCosmeticActor->Get(ActiveSkinOffset) = OverrideBattleBusSkin; + } + } + } + } + float Duration = 0; float EarlyDuration = Duration; @@ -583,9 +668,6 @@ DWORD WINAPI Main(LPVOID) LOG_INFO(LogDev, "Starting bus!"); - auto GameMode = (AFortGameMode*)GetWorld()->GetGameMode(); - auto GameState = GameMode->GetGameState(); - GameState->Get("WarmupCountdownEndTime") = 0; GameMode->Get("WarmupCountdownDuration") = 0; @@ -603,30 +685,37 @@ DWORD WINAPI Main(LPVOID) else if (GetAsyncKeyState(VK_F10) & 1) { - FString LevelA = Engine_Version < 424 - ? L"open Athena_Terrain" : Engine_Version >= 500 ? Engine_Version >= 501 - ? L"open Asteria_Terrain" - : Globals::bCreative ? L"open Creative_NoApollo_Terrain" - : L"open Artemis_Terrain" - : Globals::bCreative ? L"open Creative_NoApollo_Terrain" - : L"open Apollo_Terrain"; - - static auto BeaconClass = FindObject(L"/Script/FortniteGame.FortOnlineBeaconHost"); - auto AllFortBeacons = UGameplayStatics::GetAllActorsOfClass(GetWorld(), BeaconClass); - - for (int i = 0; i < AllFortBeacons.Num(); i++) + if (Engine_Version < 424) { - AllFortBeacons.at(i)->K2_DestroyActor(); + FString LevelA = Engine_Version < 424 + ? L"open Athena_Terrain" : Engine_Version >= 500 ? Engine_Version >= 501 + ? L"open Asteria_Terrain" + : Globals::bCreative ? L"open Creative_NoApollo_Terrain" + : L"open Artemis_Terrain" + : Globals::bCreative ? L"open Creative_NoApollo_Terrain" + : L"open Apollo_Terrain"; + + static auto BeaconClass = FindObject(L"/Script/FortniteGame.FortOnlineBeaconHost"); + auto AllFortBeacons = UGameplayStatics::GetAllActorsOfClass(GetWorld(), BeaconClass); + + for (int i = 0; i < AllFortBeacons.Num(); i++) + { + AllFortBeacons.at(i)->K2_DestroyActor(); + } + + AllFortBeacons.Free(); + + LOG_INFO(LogDev, "Switching!"); + ((AGameMode*)GetWorld()->GetGameMode())->RestartGame(); + // UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), LevelA, nullptr); + // UGameplayStatics::OpenLevel(GetWorld(), UKismetStringLibrary::Conv_StringToName(LevelA), true, FString()); + LOG_INFO(LogGame, "Restarting!"); + AmountOfRestarts++; + } + else + { + LOG_ERROR(LogGame, "Restarting is not supported on chapter 2 and above!"); } - - AllFortBeacons.Free(); - - LOG_INFO(LogDev, "Switching!"); - ((AGameMode*)GetWorld()->GetGameMode())->RestartGame(); - // UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), LevelA, nullptr); - // UGameplayStatics::OpenLevel(GetWorld(), UKismetStringLibrary::Conv_StringToName(LevelA), true, FString()); - LOG_INFO(LogDev, "Restarting!"); - AmountOfRestarts++; } Sleep(1000 / 30); diff --git a/Project Reboot 3.0/finder.h b/Project Reboot 3.0/finder.h index 3e746bc..8899951 100644 --- a/Project Reboot 3.0/finder.h +++ b/Project Reboot 3.0/finder.h @@ -325,7 +325,7 @@ static inline uint64 FindGetPlayerViewpoint() LOG_INFO(LogDev, "GetPlayerViewpoint StringRef: 0x{:x}", __int64(Addrr) - __int64(GetModuleHandleW(0))); - for (int i = 0; i < 1200; i++) + for (int i = 0; i < 1000; i++) { if (*(uint8_t*)(uint8_t*)(Addrr - i) == 0x40 && *(uint8_t*)(uint8_t*)(Addrr - i + 1) == 0x55) { @@ -337,8 +337,9 @@ static inline uint64 FindGetPlayerViewpoint() return Addrr - i; } - if (*(uint8_t*)(uint8_t*)(Addrr - i) == 0xC3) // hmm + if (Fortnite_Version == 7.20 && *(uint8_t*)(uint8_t*)(Addrr - i) == 0xC3) // hmm scuffed lmfao { + LOG_INFO(LogDev, "Hit C3!"); break; } }