From 50ab07dd1f02c86d33ea85a955d5a2af965b3fc6 Mon Sep 17 00:00:00 2001 From: Milxnor Date: Thu, 6 Apr 2023 18:27:24 -0400 Subject: [PATCH] fix s11 idk --- Project Reboot 3.0/Actor.cpp | 14 + Project Reboot 3.0/Actor.h | 11 +- Project Reboot 3.0/Array.h | 78 +++ Project Reboot 3.0/Channel.h | 2 + Project Reboot 3.0/DataChannel.cpp | 16 + Project Reboot 3.0/FortGameModeAthena.cpp | 60 ++- Project Reboot 3.0/FortItemDefinition.cpp | 2 +- Project Reboot 3.0/FortLootPackage.cpp | 13 +- Project Reboot 3.0/FortPickup.h | 6 + Project Reboot 3.0/FortPlayerController.cpp | 2 +- Project Reboot 3.0/FortQuickBars.h | 6 + Project Reboot 3.0/GenericPlatformMath.h | 6 + Project Reboot 3.0/Level.cpp | 10 + Project Reboot 3.0/Level.h | 1 + Project Reboot 3.0/Map.h | 2 + Project Reboot 3.0/NetConnection.h | 15 +- Project Reboot 3.0/NetDriver.cpp | 508 +++++++++++------- Project Reboot 3.0/NetDriver.h | 25 +- Project Reboot 3.0/Object.h | 8 +- Project Reboot 3.0/Project Reboot 3.0.vcxproj | 1 + .../Project Reboot 3.0.vcxproj.filters | 3 + Project Reboot 3.0/Set.h | 1 + Project Reboot 3.0/SparseArray.h | 7 + Project Reboot 3.0/UnrealString.h | 1 + Project Reboot 3.0/WeakObjectPtr.h | 2 +- Project Reboot 3.0/World.cpp | 36 +- Project Reboot 3.0/World.h | 9 +- Project Reboot 3.0/dllmain.cpp | 13 +- Project Reboot 3.0/finder.h | 10 +- Project Reboot 3.0/hooking.h | 3 + Project Reboot 3.0/reboot.h | 6 +- 31 files changed, 642 insertions(+), 235 deletions(-) create mode 100644 Project Reboot 3.0/DataChannel.cpp diff --git a/Project Reboot 3.0/Actor.cpp b/Project Reboot 3.0/Actor.cpp index f0d5b5c..17b7018 100644 --- a/Project Reboot 3.0/Actor.cpp +++ b/Project Reboot 3.0/Actor.cpp @@ -174,6 +174,20 @@ const AActor* AActor::GetNetOwner() const return GetNetOwnerOriginal(this); } +void AActor::GetActorEyesViewPoint(FVector* OutLocation, FRotator* OutRotation) const +{ + static auto GetActorEyesViewPointFn = FindObject("/Script/Engine.Actor.GetActorEyesViewPoint"); + struct + { + struct FVector OutLocation; // (Parm, OutParm, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + struct FRotator OutRotation; // (Parm, OutParm, ZeroConstructor, IsPlainOldData, NoDestructor, NativeAccessSpecifierPublic) + } AActor_GetActorEyesViewPoint_Params{}; + this->ProcessEvent(GetActorEyesViewPointFn, &AActor_GetActorEyesViewPoint_Params); + + *OutLocation = AActor_GetActorEyesViewPoint_Params.OutLocation; + *OutRotation = AActor_GetActorEyesViewPoint_Params.OutRotation; +} + 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 0c73713..b5d24f3 100644 --- a/Project Reboot 3.0/Actor.h +++ b/Project Reboot 3.0/Actor.h @@ -44,11 +44,18 @@ public: float& GetNetUpdateFrequency(); float& GetMinNetUpdateFrequency(); const AActor* GetNetOwner() const; + void GetActorEyesViewPoint(FVector* OutLocation, FRotator* OutRotation) const; bool IsRelevancyOwnerFor(const AActor* ReplicatedActor, const AActor* ActorOwner, const AActor* ConnectionActor) const { - // we should call virtual function but eh - return (ActorOwner == this); + // we should call virtual function but eh + // return (ActorOwner == this); + + static auto IsRelevancyOwnerForOffset = 0x428; + bool (*IsRelevancyOwnerForOriginal)(const AActor* Actor, const AActor * ReplicatedActor, const AActor * ActorOwner, const AActor * ConnectionActor) = + decltype(IsRelevancyOwnerForOriginal)(this->VFTable[IsRelevancyOwnerForOffset / 8]); + + return IsRelevancyOwnerForOriginal(this, ReplicatedActor, ActorOwner, ConnectionActor); } static class UClass* StaticClass(); diff --git a/Project Reboot 3.0/Array.h b/Project Reboot 3.0/Array.h index 241dbec..325f631 100644 --- a/Project Reboot 3.0/Array.h +++ b/Project Reboot 3.0/Array.h @@ -30,11 +30,89 @@ public: inline int Num() const { return ArrayNum; } inline int size() const { return ArrayNum; } + /* FORCENOINLINE void ResizeTo(int32 NewMax) + { + if (NewMax) + { + NewMax = AllocatorInstance.CalculateSlackReserve(NewMax, sizeof(ElementType)); + } + if (NewMax != ArrayMax) + { + ArrayMax = NewMax; + AllocatorInstance.ResizeAllocation(ArrayNum, ArrayMax, sizeof(ElementType)); + } + } + + void Empty(int32 Slack = 0) + { + // DestructItems(GetData(), ArrayNum); + + // checkSlow(Slack >= 0); + ArrayNum = 0; + + if (ArrayMax != Slack) + { + ResizeTo(Slack); + } + } + + void Reset(int32 NewSize = 0) + { + // If we have space to hold the excepted size, then don't reallocate + if (NewSize <= ArrayMax) + { + // DestructItems(GetData(), ArrayNum); + ArrayNum = 0; + } + else + { + Empty(NewSize); + } + } */ + + void RemoveAtImpl(int32 Index, int32 Count, bool bAllowShrinking) + { + if (Count) + { + // CheckInvariants(); + // checkSlow((Count >= 0) & (Index >= 0) & (Index + Count <= ArrayNum)); + + // DestructItems(GetData() + Index, Count); // TODO milxnor + + // Skip memmove in the common case that there is nothing to move. + int32 NumToMove = ArrayNum - Index - Count; + if (NumToMove) + { + /* FMemory::Memmove + ( + (uint8*)AllocatorInstance.GetAllocation() + (Index) * sizeof(ElementType), + (uint8*)AllocatorInstance.GetAllocation() + (Index + Count) * sizeof(ElementType), + NumToMove * sizeof(ElementType) + ); */ + // memmove(Data + (Index) * sizeof(InElementType), Data + (Index + Count) * sizeof(InElementType), NumToMove * sizeof(InElementType)); // i think this wrong + } + ArrayNum -= Count; + + if (bAllowShrinking) + { + // ResizeShrink(); // TODO milxnor + } + } + } + + FORCEINLINE SizeType CalculateSlackGrow(SizeType NumElements, SizeType NumAllocatedElements, SIZE_T NumBytesPerElement) const { return ArrayMax - NumElements; } + template + FORCEINLINE void RemoveAt(int32 Index, CountType Count, bool bAllowShrinking = true) + { + // static_assert(!TAreTypesEqual::Value, "TArray::RemoveAt: unexpected bool passed as the Count argument"); + RemoveAtImpl(Index, Count, bAllowShrinking); + } + void Reserve(int Number, size_t Size = sizeof(InElementType)) { // LOG_INFO(LogDev, "ArrayNum {}", ArrayNum); diff --git a/Project Reboot 3.0/Channel.h b/Project Reboot 3.0/Channel.h index 7d7be52..f110fd8 100644 --- a/Project Reboot 3.0/Channel.h +++ b/Project Reboot 3.0/Channel.h @@ -22,4 +22,6 @@ public: static auto BitfieldOffset = GetOffset("Connection") + 8; return ((PlaceholderBitfield*)(__int64(this) + BitfieldOffset))->Third; } + + int32 IsNetReady(bool Saturate); }; \ No newline at end of file diff --git a/Project Reboot 3.0/DataChannel.cpp b/Project Reboot 3.0/DataChannel.cpp new file mode 100644 index 0000000..0502104 --- /dev/null +++ b/Project Reboot 3.0/DataChannel.cpp @@ -0,0 +1,16 @@ +#include "Channel.h" +#include "NetConnection.h" + +int32 UChannel::IsNetReady(bool Saturate) +{ + static auto NumOutRecOffset = 0x4C; + + if (*(int*)(__int64(this) + NumOutRecOffset) < 255) + { + static auto ConnectionOffset = GetOffset("Connection"); + auto Connection = Get(ConnectionOffset); + return Connection->IsNetReady(Saturate); + } + + return 0; +} \ No newline at end of file diff --git a/Project Reboot 3.0/FortGameModeAthena.cpp b/Project Reboot 3.0/FortGameModeAthena.cpp index 01c2d59..4381180 100644 --- a/Project Reboot 3.0/FortGameModeAthena.cpp +++ b/Project Reboot 3.0/FortGameModeAthena.cpp @@ -39,7 +39,6 @@ enum class EDynamicFoundationType : uint8_t EDynamicFoundationType_MAX = 4 }; - static UObject* GetPlaylistToUse() { auto Playlist = FindObject("/Game/Athena/Playlists/Playlist_DefaultSolo.Playlist_DefaultSolo"); @@ -65,6 +64,8 @@ static UObject* GetPlaylistToUse() // SET OVERRIDE PLAYLIST DOWN HERE + Playlist = FindObject("/Game/Athena/Playlists/Playlist_DefaultDuo.Playlist_DefaultDuo"); + // Playlist = FindObject("/Game/Athena/Playlists/Playground/Playlist_Playground.Playlist_Playground"); // Playlist = FindObject("/Game/Athena/Playlists/Carmine/Playlist_Carmine.Playlist_Carmine"); @@ -274,20 +275,23 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game GameMode->Get("WarmupRequiredPlayerCount") = 1; - auto PlaylistToUse = GetPlaylistToUse(); + if (Fortnite_Version >= 3) // idk when they switched off id + { + auto PlaylistToUse = GetPlaylistToUse(); - if (!PlaylistToUse) - { - LOG_ERROR(LogPlaylist, "Failed to find playlist! Proceeding, but will probably not work as expected!"); - } - else - { - if (Fortnite_Version >= 4) + if (!PlaylistToUse) { - SetPlaylist(PlaylistToUse, true); + LOG_ERROR(LogPlaylist, "Failed to find playlist! Proceeding, but will probably not work as expected!"); + } + 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"); + } } } @@ -548,10 +552,13 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game GameSession->Get(MaxPlayersOffset) = 100; // if (Engine_Version < 424) - GameState->OnRep_CurrentPlaylistInfo(); // ? + GameState->OnRep_CurrentPlaylistInfo(); // ? // SetupNavConfig(); + static auto bAlwaysDBNOOffset = GameMode->GetOffset("bAlwaysDBNO"); + GameMode->Get(bAlwaysDBNOOffset) = true; + LOG_INFO(LogDev, "Initialized!"); } @@ -628,6 +635,7 @@ int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint CurrentTeamMembers = 0; LOG_INFO(LogTeams, "Player is going on team {} with {} members (No Playlist).", Current, CurrentTeamMembers); CurrentTeamMembers++; + return Current; return Current++; } @@ -693,15 +701,15 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena bool bPrintWarmup = false; - if (Engine_Version != 419) + for (int i = 0; i < SpawnIsland_FloorLoot_Actors.Num(); i++) { - for (int i = 0; i < SpawnIsland_FloorLoot_Actors.Num(); i++) + ABuildingContainer* CurrentActor = (ABuildingContainer*)SpawnIsland_FloorLoot_Actors.at(i); + + // CurrentActor->K2_DestroyActor(); + // continue; + + if (Engine_Version != 419) { - ABuildingContainer* CurrentActor = (ABuildingContainer*)SpawnIsland_FloorLoot_Actors.at(i); - - // CurrentActor->K2_DestroyActor(); - // continue; - auto Location = CurrentActor->GetActorLocation(); Location.Z += UpZ; @@ -715,11 +723,13 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena if (LootDrops.size()) { for (auto& LootDrop : LootDrops) - AFortPickup::SpawnPickup(LootDrop.ItemDefinition, Location, LootDrop.Count, SpawnFlag, EFortPickupSpawnSource::Unset, LootDrop.LoadedAmmo); + { + auto Pickup = AFortPickup::SpawnPickup(LootDrop.ItemDefinition, Location, LootDrop.Count, SpawnFlag, EFortPickupSpawnSource::Unset, LootDrop.LoadedAmmo); + } } - - CurrentActor->K2_DestroyActor(); } + + CurrentActor->K2_DestroyActor(); } bool bPrint = false; @@ -745,7 +755,9 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena if (LootDrops.size()) { for (auto& LootDrop : LootDrops) - AFortPickup::SpawnPickup(LootDrop.ItemDefinition, Location, LootDrop.Count, SpawnFlag, EFortPickupSpawnSource::Unset, LootDrop.LoadedAmmo); + { + auto Pickup = AFortPickup::SpawnPickup(LootDrop.ItemDefinition, Location, LootDrop.Count, SpawnFlag, EFortPickupSpawnSource::Unset, LootDrop.LoadedAmmo); + } } CurrentActor->K2_DestroyActor(); diff --git a/Project Reboot 3.0/FortItemDefinition.cpp b/Project Reboot 3.0/FortItemDefinition.cpp index 4933874..cce127f 100644 --- a/Project Reboot 3.0/FortItemDefinition.cpp +++ b/Project Reboot 3.0/FortItemDefinition.cpp @@ -16,7 +16,7 @@ float UFortItemDefinition::GetMaxStackSize() { static auto MaxStackSizeOffset = this->GetOffset("MaxStackSize"); - bool bIsScalableFloat = Engine_Version >= 424; // idk + bool bIsScalableFloat = Fortnite_Version >= 12; // idk if (!bIsScalableFloat) return Get(MaxStackSizeOffset); diff --git a/Project Reboot 3.0/FortLootPackage.cpp b/Project Reboot 3.0/FortLootPackage.cpp index 13af7dc..af87095 100644 --- a/Project Reboot 3.0/FortLootPackage.cpp +++ b/Project Reboot 3.0/FortLootPackage.cpp @@ -400,13 +400,9 @@ std::vector PickLootDrops(FName TierGroupName, bool bPrint, int recurs } auto& LTDRowMap = LTD->GetRowMap(); - auto LTDRowMapNum = LTDRowMap.Pairs.Elements.Num(); - // auto TierGroupNameStr = TierGroupName.ToString(); - - for (int i = 0; i < LTDRowMapNum; i++) + for (auto& CurrentLTD : LTDRowMap) { - auto& CurrentLTD = LTDRowMap.Pairs.Elements[i].ElementData.Value; auto TierData = (FFortLootTierData*)CurrentLTD.Value(); if (IsBadReadPtr(TierData, 8)) // this shouldn't be needed @@ -516,9 +512,8 @@ std::vector PickLootDrops(FName TierGroupName, bool bPrint, int recurs auto& LPRowMap = LP->GetRowMap(); - for (int i = 0; i < LPRowMap.Pairs.Elements.Num(); i++) + for (auto& CurrentLP : LPRowMap) { - auto& CurrentLP = LPRowMap.Pairs.Elements[i].ElementData.Value; auto LootPackage = (FFortLootPackageData*)CurrentLP.Value(); if (!LootPackage) @@ -618,10 +613,8 @@ std::vector PickLootDrops(FName TierGroupName, bool bPrint, int recurs auto& LPRowMap = LPTables[p]->GetRowMap(); - for (int j = 0; j < LPRowMap.Pairs.Elements.Num(); j++) + for (auto& CurrentLP : LPRowMap) { - auto& CurrentLP = LPRowMap.Pairs.Elements[j].ElementData.Value; - auto LootPackage = (FFortLootPackageData*)CurrentLP.Value(); if (LootPackage->GetLootPackageID().ToString() == TierGroupLPStr && LootPackage->GetWeight() != 0 && LootPackage->GetCount() != 0) diff --git a/Project Reboot 3.0/FortPickup.h b/Project Reboot 3.0/FortPickup.h index 09f34d3..426e8a0 100644 --- a/Project Reboot 3.0/FortPickup.h +++ b/Project Reboot 3.0/FortPickup.h @@ -69,6 +69,12 @@ public: void TossPickup(FVector FinalLocation, class AFortPawn* ItemOwner, int OverrideMaxStackCount, bool bToss, EFortPickupSourceTypeFlag InPickupSourceTypeFlags, EFortPickupSpawnSource InPickupSpawnSource); + void OnRep_PrimaryPickupItemEntry() + { + static auto OnRep_PrimaryPickupItemEntryFn = FindObject("/Script/FortniteGame.FortPickup.OnRep_PrimaryPickupItemEntry"); + this->ProcessEvent(OnRep_PrimaryPickupItemEntryFn); + } + FFortPickupLocationData* GetPickupLocationData() { static auto PickupLocationDataOffset = this->GetOffset("PickupLocationData"); diff --git a/Project Reboot 3.0/FortPlayerController.cpp b/Project Reboot 3.0/FortPlayerController.cpp index 433a0a8..6e16860 100644 --- a/Project Reboot 3.0/FortPlayerController.cpp +++ b/Project Reboot 3.0/FortPlayerController.cpp @@ -930,7 +930,7 @@ void AFortPlayerController::ServerEditBuildingActorHook(UObject* Context, FFrame if (!BuildingActorToEdit || !NewBuildingClass || BuildingActorToEdit->IsDestroyed() || BuildingActorToEdit->GetEditingPlayer() != PlayerState) { LOG_INFO(LogDev, "Cheater?"); - LOG_INFO(LogDev, "BuildingActorToEdit->GetEditingPlayer(): {} PlayerState: {} NewBuildingClass: {} BuildingActorToEdit: {}", __int64(BuildingActorToEdit->GetEditingPlayer()), __int64(PlayerState), __int64(NewBuildingClass), __int64(BuildingActorToEdit)); + LOG_INFO(LogDev, "BuildingActorToEdit->GetEditingPlayer(): {} PlayerState: {} NewBuildingClass: {} BuildingActorToEdit: {}", BuildingActorToEdit ? __int64(BuildingActorToEdit->GetEditingPlayer()) : -1, __int64(PlayerState), __int64(NewBuildingClass), __int64(BuildingActorToEdit)); return ServerEditBuildingActorOriginal(Context, Stack, Ret); } diff --git a/Project Reboot 3.0/FortQuickBars.h b/Project Reboot 3.0/FortQuickBars.h index 380b26b..1057dab 100644 --- a/Project Reboot 3.0/FortQuickBars.h +++ b/Project Reboot 3.0/FortQuickBars.h @@ -116,4 +116,10 @@ public: return -1; } + + static UClass* StaticClass() + { + static auto Class = FindObject("/Script/FortniteGame.FortQuickBars"); + return Class; + } }; \ No newline at end of file diff --git a/Project Reboot 3.0/GenericPlatformMath.h b/Project Reboot 3.0/GenericPlatformMath.h index c8725a9..7c08def 100644 --- a/Project Reboot 3.0/GenericPlatformMath.h +++ b/Project Reboot 3.0/GenericPlatformMath.h @@ -15,6 +15,12 @@ public: return (float)TruncToInt(F); } + template< class T > + static constexpr FORCEINLINE T Min(const T A, const T B) + { + return (A <= B) ? A : B; + } + static FORCEINLINE int32 FloorToInt(float F) { return TruncToInt(floorf(F)); diff --git a/Project Reboot 3.0/Level.cpp b/Project Reboot 3.0/Level.cpp index d75de64..a5cbc33 100644 --- a/Project Reboot 3.0/Level.cpp +++ b/Project Reboot 3.0/Level.cpp @@ -22,4 +22,14 @@ bool ULevel::HasVisibilityChangeRequestPending() auto CurrentLevelPendingInvisibility = OwningWorld->Get(CurrentLevelPendingInvisibilityOffset); return this == CurrentLevelPendingVisibility || this == CurrentLevelPendingInvisibility; +} + +AWorldSettings* ULevel::GetWorldSettings(bool bChecked) const +{ + if (bChecked) + { + // checkf(WorldSettings != nullptr, TEXT("%s"), *GetPathName()); + } + static auto WorldSettingsOffset = GetOffset("WorldSettings"); + return Get(WorldSettingsOffset); } \ No newline at end of file diff --git a/Project Reboot 3.0/Level.h b/Project Reboot 3.0/Level.h index 314da12..7cafb9c 100644 --- a/Project Reboot 3.0/Level.h +++ b/Project Reboot 3.0/Level.h @@ -7,4 +7,5 @@ class ULevel : public UObject public: UWorld*& GetOwningWorld(); bool HasVisibilityChangeRequestPending(); + AWorldSettings* GetWorldSettings(bool bChecked = true) const; }; \ No newline at end of file diff --git a/Project Reboot 3.0/Map.h b/Project Reboot 3.0/Map.h index a86b063..46fc26e 100644 --- a/Project Reboot 3.0/Map.h +++ b/Project Reboot 3.0/Map.h @@ -133,6 +133,8 @@ public: return Pair.Value(); } } + + LOG_INFO(LogDev, "Failed to find Key!!!"); } FORCEINLINE ValueType& Find(const KeyType& Key) { diff --git a/Project Reboot 3.0/NetConnection.h b/Project Reboot 3.0/NetConnection.h index 3996c4b..09c2719 100644 --- a/Project Reboot 3.0/NetConnection.h +++ b/Project Reboot 3.0/NetConnection.h @@ -27,6 +27,19 @@ public: return Get(ViewTargetOffset); } + int32 IsNetReady(bool Saturate) + { + static auto IsNetReadyOffset = 0x298; // 1.11 + int32 (*IsNetReadyOriginal)(UNetConnection* Connection, bool Saturate) = decltype(IsNetReadyOriginal)(this->VFTable[IsNetReadyOffset / 8]); + return IsNetReadyOriginal(this, Saturate); + } + + double& GetLastReceiveTime() + { + static auto LastReceiveTimeOffset = GetOffset("LastReceiveTime"); + return Get(LastReceiveTimeOffset); + } + TSet& GetClientVisibleLevelNames() { static auto ClientVisibleLevelNamesOffset = 0x336C8; @@ -59,7 +72,7 @@ public: bool ClientHasInitializedLevelFor(const AActor* TestActor) const { - bool (*ClientHasInitializedLevelForOriginal)(const UNetConnection* Connection, const AActor * TestActor) = decltype(ClientHasInitializedLevelForOriginal)(this->VFTable[0x300 / 8]); + bool (*ClientHasInitializedLevelForOriginal)(const UNetConnection* Connection, const AActor* TestActor) = decltype(ClientHasInitializedLevelForOriginal)(this->VFTable[0x300 / 8]); return ClientHasInitializedLevelForOriginal(this, TestActor); } }; \ No newline at end of file diff --git a/Project Reboot 3.0/NetDriver.cpp b/Project Reboot 3.0/NetDriver.cpp index d4dfe1a..1612db2 100644 --- a/Project Reboot 3.0/NetDriver.cpp +++ b/Project Reboot 3.0/NetDriver.cpp @@ -11,6 +11,69 @@ #include "ActorChannel.h" #include "KismetSystemLibrary.h" #include "UnrealMathUtility.h" +#include "FortQuickBars.h" + +void UNetDriver::TickFlushHook(UNetDriver* NetDriver) +{ + static auto ReplicationDriverOffset = NetDriver->GetOffset("ReplicationDriver", false); + + if (ReplicationDriverOffset == -1) + { + NetDriver->ServerReplicateActors(); + } + else + { + if (auto ReplicationDriver = NetDriver->Get(ReplicationDriverOffset)) + reinterpret_cast(ReplicationDriver->VFTable[Offsets::ServerReplicateActors])(ReplicationDriver); + } + + return TickFlushOriginal(NetDriver); +} + +void Test3(AActor* Actor, const std::string& Str) +{ + if (Actor->IsA(AFortPawn::StaticClass())) + { + LOG_INFO(LogDev, "[{}] Pawn", Str) + } + if (Actor->IsA(AFortQuickBars::StaticClass())) + { + LOG_INFO(LogDev, "[{}] QuickBars", Str) + } +} + +#define NAME_None 0 + +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); + + newViewer.ViewDir = ViewRotation.Vector(); + } + + return newViewer; +} FNetworkObjectList& UNetDriver::GetNetworkObjectList() { @@ -31,6 +94,58 @@ struct FPacketIdRange } }; +float GetTimeSecondsForWorld(UWorld* World) +{ + static auto TimeSecondsOffset = 0x900; + return *(float*)(__int64(World) + TimeSecondsOffset); +} + +void SetChannelActorForDestroy(UActorChannel* ActorChannel, FActorDestructionInfo* DestructInfo) +{ + static auto ConnectionOffset = ActorChannel->GetOffset("Connection"); + UNetConnection* Connection = ActorChannel->Get(ConnectionOffset); + + auto State = *(uint8_t*)(__int64(Connection) + 0x12C); + + if (!(State - 2 <= 1)) // this will make sure that it is USOCK_Open or USOCK_Pending + return; + + using FOutBunch = __int64; + + static auto PackageMapOffset = Connection->GetOffset("PackageMap"); + auto PackageMap = Connection->Get(PackageMapOffset); + + FOutBunch* CloseBunch = Alloc(0x200); + + if (!CloseBunch) + return; + + static FOutBunch(*FOutBunchConstructor)(FOutBunch * a1, UActorChannel * a2, bool bInClose) = decltype(FOutBunchConstructor)(__int64(GetModuleHandleW(0)) + 0x194E800); + FPacketIdRange Range(0); + FPacketIdRange* (*SendBunchOriginal)(UActorChannel * Channel, FPacketIdRange * OutRange, FOutBunch * Bunch, bool Merge) = decltype(SendBunchOriginal)(ActorChannel->VFTable[0x288 / 8]); + bool (*UPackageMap_WriteObjectOriginal)(UObject * PackageMap, FOutBunch * Ar, UObject * InOuter, FNetworkGUID NetGUID, FString ObjectName) = decltype(UPackageMap_WriteObjectOriginal)(PackageMap->VFTable[0x238 / 8]); + static void (*FArchiveDeconstructor)(FOutBunch * Ar) = decltype(FArchiveDeconstructor)(__int64(GetModuleHandleW(0)) + 0xC36500); + + FOutBunchConstructor(CloseBunch, ActorChannel, true); + + // we could set bDormant but it's set by default to 0. + SetBitfield((PlaceholderBitfield*)(__int64(CloseBunch) + 0x30), 4, true); // bReliable + + /* LOG_INFO(LogDev, "UPackageMap_WriteObjectOriginal: 0x{:x}", __int64(UPackageMap_WriteObjectOriginal) - __int64(GetModuleHandleW(0))); + LOG_INFO(LogDev, "DestructInfo->PathName: {} Num: {} Max: {} Data: {}", DestructInfo->PathName.ToString(), DestructInfo->PathName.Data.Num(), DestructInfo->PathName.Data.ArrayMax, __int64(DestructInfo->PathName.Data.Data)); + // LOG_INFO(LogDev, "DestructInfo->ObjOuter: {}", DestructInfo->ObjOuter.Get()->IsValidLowLevel() ? DestructInfo->ObjOuter.Get()->GetFullName() : "BadRead"); + + if (DestructInfo->PathName.ToString() == "???") + return; */ + + UPackageMap_WriteObjectOriginal(PackageMap, CloseBunch, DestructInfo->ObjOuter.Get(), DestructInfo->NetGUID, DestructInfo->PathName); + SendBunchOriginal(ActorChannel, &Range, CloseBunch, false); + + FArchiveDeconstructor(CloseBunch); +} + +int CVarSetNetDormancyEnabled = 1; // idk what we supposed to set this to + #define CLOSEPROXIMITY 500.f #define NEARSIGHTTHRESHOLD 2000.f #define MEDSIGHTTHRESHOLD 3162.f @@ -84,43 +199,6 @@ FActorPriority::FActorPriority(UNetConnection* InConnection, FActorDestructionIn } } -void SetChannelActorForDestroy(UActorChannel* ActorChannel, FActorDestructionInfo* DestructInfo) -{ - static auto ConnectionOffset = ActorChannel->GetOffset("Connection"); - UNetConnection* Connection = ActorChannel->Get(ConnectionOffset); - - auto State = *(uint8_t*)(__int64(Connection) + 0x12C); - - if (!(State - 2 <= 1)) // this will make sure that it is USOCK_Open or USOCK_Pending - return; - - using FOutBunch = __int64; - - static auto PackageMapOffset = Connection->GetOffset("PackageMap"); - auto PackageMap = Connection->Get(PackageMapOffset); - - FOutBunch* CloseBunch = Alloc(0x200); - - if (!CloseBunch) - return; - - static FOutBunch(*FOutBunchConstructor)(FOutBunch* a1, UActorChannel* a2, bool bInClose) = decltype(FOutBunchConstructor)(__int64(GetModuleHandleW(0)) + 0x194E800); - /* *CloseBunch = */ FOutBunchConstructor(CloseBunch, ActorChannel, true); - - // we could set bDormant but it's set by default to 0. - - SetBitfield((PlaceholderBitfield*)(__int64(CloseBunch) + 0x30), 4, true); // bReliable - - bool (*UPackageMap_WriteObjectOriginal)(UObject* PackageMap, FOutBunch* Ar, UObject* InOuter, FNetworkGUID NetGUID, FString ObjectName) = decltype(UPackageMap_WriteObjectOriginal)(PackageMap->VFTable[0x238 / 8]); - UPackageMap_WriteObjectOriginal(PackageMap, CloseBunch, DestructInfo->ObjOuter.Get(), DestructInfo->NetGUID, DestructInfo->PathName); - - FPacketIdRange (*SendBunchOriginal)(UActorChannel* Channel, FOutBunch* Bunch, bool Merge) = decltype(SendBunchOriginal)(ActorChannel->VFTable[0x288 / 8]); - SendBunchOriginal(ActorChannel, CloseBunch, false); - - static void (*FArchiveDeconstructor)(FOutBunch* Ar) = decltype(FArchiveDeconstructor)(__int64(GetModuleHandleW(0)) + 0xC36500); - FArchiveDeconstructor(CloseBunch); -} - TSet& GetConnectionDestroyedStartupOrDormantActors(UNetConnection* Connection) { return *(TSet*)(__int64(Connection) + 0x33678); @@ -140,22 +218,7 @@ void UNetDriver::RemoveNetworkActor(AActor* Actor) // RenamedStartupActors.Remove(Actor->GetFName()); } -void UNetDriver::TickFlushHook(UNetDriver* NetDriver) -{ - static auto ReplicationDriverOffset = NetDriver->GetOffset("ReplicationDriver", false); - - if (ReplicationDriverOffset == -1) - { - NetDriver->ServerReplicateActors(); - } - else - { - if (auto ReplicationDriver = NetDriver->Get(ReplicationDriverOffset)) - reinterpret_cast(ReplicationDriver->VFTable[Offsets::ServerReplicateActors])(ReplicationDriver); - } - - return TickFlushOriginal(NetDriver); -} +int MaxConnectionsToTickPerServerFrame = 25; int32 ServerReplicateActors_PrepConnections(UNetDriver* NetDriver) { @@ -163,6 +226,9 @@ int32 ServerReplicateActors_PrepConnections(UNetDriver* NetDriver) int32 NumClientsToTick = ClientConnections.Num(); + if (MaxConnectionsToTickPerServerFrame > 0) + NumClientsToTick = FMath::Min(ClientConnections.Num(), MaxConnectionsToTickPerServerFrame); + bool bFoundReadyConnection = false; for (int32 ConnIdx = 0; ConnIdx < ClientConnections.Num(); ConnIdx++) @@ -174,8 +240,10 @@ int32 ServerReplicateActors_PrepConnections(UNetDriver* NetDriver) AActor* OwningActor = Connection->GetOwningActor(); - if (OwningActor != NULL) // && /* Connection->State == USOCK_Open && */ (Connection->Driver->Time - Connection->LastReceiveTime < 1.5f)) + if (OwningActor != NULL && /* Connection->State == USOCK_Open && */ (Connection->GetDriver()->GetTime() - Connection->GetLastReceiveTime() < 1.5f)) { + // check( World == OwningActor->GetWorld() ); + bFoundReadyConnection = true; AActor* DesiredViewTarget = OwningActor; @@ -211,15 +279,9 @@ enum class ENetRole : uint8_t FORCEINLINE float FRand() { return ReplicationRandStream.FRand(); - - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution<> dis(0, 1); - float random_number = dis(gen); - - return random_number; } +#if 1 // below is "proper" FORCEINLINE float SRand() { GSRandSeed = (GSRandSeed * 196314165) + 907633515; @@ -233,29 +295,36 @@ FORCEINLINE float SRand() // res /= 3; return res; } +#else +FORCEINLINE float SRand() +{ + auto now = std::chrono::system_clock::now(); + auto now_ms = std::chrono::time_point_cast(now); + auto value = now_ms.time_since_epoch().count(); -void UNetDriver::ServerReplicateActors_BuildConsiderList(std::vector& OutConsiderList) + std::default_random_engine generator(value); + + std::uniform_real_distribution distribution(0.0f, 1.0f); + return distribution(generator); +} +#endif + +void UNetDriver::ServerReplicateActors_BuildConsiderList(std::vector& OutConsiderList, const float ServerTickTime) { std::vector ActorsToRemove; - auto& ActiveObjects = GetNetworkObjectList().ActiveNetworkObjects; - auto World = GetWorld(); - for (const TSharedPtr& ActorInfo : ActiveObjects) + for (const TSharedPtr& ActorInfo : GetNetworkObjectList().ActiveNetworkObjects) { - if (!ActorInfo->bPendingNetUpdate && UGameplayStatics::GetTimeSeconds(GetWorld()) <= ActorInfo->NextUpdateTime) + if (!ActorInfo->bPendingNetUpdate && GetTimeSecondsForWorld(GetWorld()) <= ActorInfo->NextUpdateTime) { continue; } auto Actor = ActorInfo->Actor; - if (!Actor) - continue; - if (Actor->IsPendingKillPending()) - // if (Actor->IsPendingKill()) { ActorsToRemove.push_back(Actor); continue; @@ -275,39 +344,40 @@ void UNetDriver::ServerReplicateActors_BuildConsiderList(std::vectorGetNetDormancy() == ENetDormancy::DORM_Initial && Actor->IsNetStartupActor()) // IsDormInitialStartupActor + if (Actor->GetNetDormancy() == ENetDormancy::DORM_Initial && Actor->IsNetStartupActor()) { ActorsToRemove.push_back(Actor); 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. + static auto NeedsLoadForClientOriginalOffset = 0xD0; + bool (*NeedsLoadForClientOriginal)(AActor* Actor) = decltype(NeedsLoadForClientOriginal)(Actor->VFTable[NeedsLoadForClientOriginalOffset / 8]); - auto TimeSeconds = UGameplayStatics::GetTimeSeconds(World); // Can we do this outside of the loop? + if (!NeedsLoadForClientOriginal(Actor)) // Should we remove? + continue; + + // 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->LastNetReplicateTime = GetTimeSecondsForWorld(World); ActorInfo->OptimalNetUpdateDelta = 1.0f / Actor->GetNetUpdateFrequency(); } const float ScaleDownStartTime = 2.0f; const float ScaleDownTimeRange = 5.0f; - const float LastReplicateDelta = TimeSeconds - ActorInfo->LastNetReplicateTime; + const float LastReplicateDelta = GetTimeSecondsForWorld(World) - ActorInfo->LastNetReplicateTime; if (LastReplicateDelta > ScaleDownStartTime) { - static auto MinNetUpdateFrequencyOffset = Actor->GetOffset("MinNetUpdateFrequency"); - - if (Actor->Get(MinNetUpdateFrequencyOffset) == 0.0f) + if (Actor->GetMinNetUpdateFrequency() == 0.0f) { - Actor->Get(MinNetUpdateFrequencyOffset) = 2.0f; + Actor->GetMinNetUpdateFrequency() = 2.0f; } const float MinOptimalDelta = 1.0f / Actor->GetNetUpdateFrequency(); // Don't go faster than NetUpdateFrequency - const float MaxOptimalDelta = FMath::Max(1.0f / Actor->GetNetUpdateFrequency(), MinOptimalDelta); // Don't go slower than MinNetUpdateFrequency (or NetUpdateFrequency if it's slower) + const float MaxOptimalDelta = FMath::Max(1.0f / Actor->GetMinNetUpdateFrequency(), MinOptimalDelta); // Don't go slower than MinNetUpdateFrequency (or NetUpdateFrequency if it's slower) const float Alpha = FMath::Clamp( (LastReplicateDelta - ScaleDownStartTime) / ScaleDownTimeRange, 0.0f, 1.0f); ActorInfo->OptimalNetUpdateDelta = FMath::Lerp(MinOptimalDelta, MaxOptimalDelta, Alpha); @@ -319,13 +389,13 @@ void UNetDriver::ServerReplicateActors_BuildConsiderList(std::vectorOptimalNetUpdateDelta : 1.0f / Actor->GetNetUpdateFrequency(); // then set the next update time - float ServerTickTime = 1.f / 30; - ActorInfo->NextUpdateTime = TimeSeconds + SRand() * ServerTickTime + NextUpdateDelta; + ActorInfo->NextUpdateTime = GetTimeSecondsForWorld(World) + SRand() * ServerTickTime + NextUpdateDelta; ActorInfo->LastNetUpdateTime = GetTime(); } ActorInfo->bPendingNetUpdate = false; + // ensure( OutConsiderList.Num() < OutConsiderList.Max() ); OutConsiderList.push_back(ActorInfo.Get()); static void (*CallPreReplication)(AActor*, UNetDriver*) = decltype(CallPreReplication)(Addresses::CallPreReplication); @@ -446,6 +516,61 @@ static FORCEINLINE bool ShouldActorGoDormant(AActor* Actor, const std::vectorGetActorChannels(); + + for (auto& Pair : ActorChannels) + { + if (Pair.First == ActorInfo->WeakActor) + { + return Pair.Second; + } + } + + return nullptr; + } + + auto Actor = ActorInfo->Actor; + + if (!Actor) + return nullptr; + + static auto OpenChannelsOffset = Connection->GetOffset("OpenChannels"); + auto& OpenChannels = Connection->Get>(OpenChannelsOffset); + + static auto ActorChannelClass = FindObject("/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)) + // continue; + + if (Channel->ClassPrivate != ActorChannelClass) + continue; + + static auto ActorOffset = Channel->GetOffset("Actor"); + + if (Channel->Get(ActorOffset) != Actor) + continue; + + return (UActorChannel*)Channel; + } + + return NULL; +} + int32 UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection* Connection, const std::vector& ConnectionViewers, const std::vector ConsiderList, const bool bCPUSaturated, FActorPriority*& OutPriorityList, FActorPriority**& OutPriorityActors) { GetNetTag()++; @@ -472,8 +597,8 @@ int32 UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection* Connect if (MaxSortedActors > 0) { - OutPriorityList = Alloc(MaxSortedActors * sizeof(FActorPriority)); - OutPriorityActors = Alloc(MaxSortedActors * sizeof(FActorPriority*)); + OutPriorityList = (FActorPriority*)FMemory::Realloc(nullptr, MaxSortedActors * sizeof(FActorPriority), 0); // Alloc(MaxSortedActors * sizeof(FActorPriority)); + OutPriorityActors = (FActorPriority**)FMemory::Realloc(nullptr, MaxSortedActors * sizeof(FActorPriority*), 0);// Alloc(MaxSortedActors * sizeof(FActorPriority*)); // check( World == Connection->ViewTarget->GetWorld() ); @@ -485,19 +610,7 @@ int32 UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection* Connect FNetworkObjectInfo* ActorInfo = ConsiderList.at(i); AActor* Actor = ActorInfo->Actor; - auto& ActorChannels = Connection->GetActorChannels(); - UActorChannel* Channel = nullptr; - - // Connection->ActorChannels.FindRef(ActorInfo->WeakActor); - - for (int i = 0; i < ActorChannels.Pairs.Num(); i++) - { - if (ActorChannels.Pairs[i].First == ActorInfo->WeakActor) - { - Channel = ActorChannels.Pairs[i].Second; - break; - } - } + UActorChannel* Channel = FindChannel(Connection, ActorInfo); if (!Channel) { @@ -509,8 +622,6 @@ int32 UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection* Connect if (!IsActorRelevantToConnection(Actor, ConnectionViewers)) { - // LOG_INFO(LogDev, "Not relevant!"); - // If not relevant (and we don't have a channel), skip continue; } @@ -537,13 +648,11 @@ int32 UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection* Connect continue; } } - // else if (CVarSetNetDormancyEnabled.GetValueOnGameThread() != 0) - else + else if (CVarSetNetDormancyEnabled != 0) { // Skip Actor if dormant if (IsActorDormant(ActorInfo, WeakConnection)) { - // LOG_INFO(LogDev, "Actor is dormant!"); continue; } @@ -568,23 +677,20 @@ int32 UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection* Connect OutPriorityActors[FinalSortedCount] = OutPriorityList + FinalSortedCount; FinalSortedCount++; + // Test3(Actor, "ryo i got added"); } } // Add in deleted actors - /* - - for (auto& CurrentGuid : Connection_DestroyedStartupOrDormantActors) + /* for (auto& CurrentGuid : Connection_DestroyedStartupOrDormantActors) { FActorDestructionInfo& DInfo = GetDriverDestroyedStartupOrDormantActors(this).Find(CurrentGuid); OutPriorityList[FinalSortedCount] = FActorPriority(Connection, &DInfo, ConnectionViewers); OutPriorityActors[FinalSortedCount] = OutPriorityList + FinalSortedCount; FinalSortedCount++; DeletedCount++; - } - - */ + } */ // Sort(OutPriorityActors, FinalSortedCount, FCompareFActorPriority()); } @@ -592,32 +698,6 @@ int32 UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection* Connect return FinalSortedCount; } -#define NAME_None 0 - -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) - { - FRotator ViewRotation = ViewingController->GetControlRotation(); - AFortPlayerControllerAthena::GetPlayerViewPointHook(Cast(ViewingController, false), newViewer.ViewLocation, ViewRotation); - newViewer.ViewDir = ViewRotation.Vector(); - } - - return newViewer; -} - int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* Connection, const std::vector& ConnectionViewers, FActorPriority** PriorityActors, const int32 FinalSortedCount, int32& OutUpdated) { static UChannel* (*CreateChannel)(UNetConnection*, int, bool, int32_t) = decltype(CreateChannel)(Addresses::CreateChannel); @@ -628,6 +708,11 @@ int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* int32 ActorUpdatesThisConnectionSent = 0; int32 FinalRelevantCount = 0; + if (!Connection->IsNetReady(0)) + { + return 0; + } + for (int32 j = 0; j < FinalSortedCount; j++) { FNetworkObjectInfo* ActorInfo = PriorityActors[j]->ActorInfo; @@ -642,14 +727,30 @@ int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* continue; } + auto& Connection_DestroyedStartupOrDormantActors = GetConnectionDestroyedStartupOrDormantActors(Connection); + + bool bFound = false; + + for (auto& aa : Connection_DestroyedStartupOrDormantActors) + { + if (aa == PriorityActors[j]->DestructionInfo->NetGUID) + { + bFound = true; + break; + } + } + + LOG_INFO(LogDev, "bFound: {}", bFound); + + if (!bFound) + continue; + UActorChannel* Channel = (UActorChannel*)CreateChannel(Connection, 2, true, -1); if (Channel) { FinalRelevantCount++; - auto& Connection_DestroyedStartupOrDormantActors = GetConnectionDestroyedStartupOrDormantActors(Connection); - SetChannelActorForDestroy(Channel, PriorityActors[j]->DestructionInfo); // Send a close bunch on the new channel Connection_DestroyedStartupOrDormantActors.Remove(PriorityActors[j]->DestructionInfo->NetGUID); // Remove from connections to-be-destroyed list (close bunch of reliable, so it will make it there) } @@ -659,6 +760,7 @@ int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* // Normal actor replication UActorChannel* Channel = PriorityActors[j]->Channel; + if (!Channel || Channel->GetActor()) //make sure didn't just close this channel { AActor* Actor = ActorInfo->Actor; @@ -672,19 +774,36 @@ int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* // bTearOff actors should never be checked if (bLevelInitializedForActor) { - if (!Actor->IsTearOff() && (!Channel || GetTime() - Channel->GetRelevantTime() > 1.f)) + // if (!Actor->IsTearOff() && (!Channel || GetTime() - Channel->GetRelevantTime() > 1.f)) + if (!Actor->IsTearOff()) { - if (IsActorRelevantToConnection(Actor, ConnectionViewers)) + // Test3(Actor, "Passed first check"); + + if ((!Channel || GetTime() - Channel->GetRelevantTime() > 1.f)) { - bIsRelevant = true; + // Test3(Actor, "Passed SECOND check"); + + if (IsActorRelevantToConnection(Actor, ConnectionViewers)) + { + // Test3(Actor, "Passed THIRD check"); + bIsRelevant = true; + } } + else + { + // Test3(Actor, "FAiled second check"); + } + } + else + { + // Test3(Actor, "Failed first check"); } } // if the actor is now relevant or was recently relevant const bool bIsRecentlyRelevant = bIsRelevant || (Channel && GetTime() - Channel->GetRelevantTime() < GetRelevantTimeout()) || ActorInfo->bForceRelevantNextUpdate; - // Test2(Actor, std::format("bIsRecentlyRelevant: {} Channel: {} bIsRelevant: {}", (int)bIsRecentlyRelevant, __int64(Channel), (int)bIsRelevant)); + // Test3(Actor, std::format("bIsRecentlyRelevant: {} bLevelInitializedForActor: {} Channel: {} bIsRelevant: {}", (int)bIsRecentlyRelevant, bLevelInitializedForActor, __int64(Channel), (int)bIsRelevant)); // Test2(Actor, std::format("bIsRelevant: {} bLevelInitializedForActor: {} Cond: {}", bIsRelevant, bLevelInitializedForActor, !Actor->IsTearOff() && (!Channel || GetTime() - Channel->GetRelevantTime() > 1.f))); // Test2(Actor, std::format("TearOff: {} GetTime(): {} Channel->GetRelevantTime(): {}", !Actor->IsTearOff(), GetTime(), Channel ? Channel->GetRelevantTime() : 99)); @@ -694,7 +813,10 @@ int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* { FinalRelevantCount++; - if (Channel == NULL) // && GuidCache->SupportsObject(Actor->GetClass()) && GuidCache->SupportsObject(Actor->IsNetStartupActor() ? Actor : Actor->GetArchetype())) + if (Channel == NULL + /* && GetGuidCache()->SupportsObject(Actor->ClassPrivate) + && GetGuidCache()->SupportsObject(Actor->IsNetStartupActor() ? Actor : Actor->GetArchetype()) */ + ) { if (bLevelInitializedForActor) { @@ -709,7 +831,7 @@ int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* // if we couldn't replicate it for a reason that should be temporary, and this Actor is updated very infrequently, make sure we update it again soon else if (Actor->GetNetUpdateFrequency() < 1.0f) { - auto TimeSeconds = UGameplayStatics::GetTimeSeconds(GetWorld()); // Actor->GetWorld()->TimeSeconds + auto TimeSeconds = GetTimeSecondsForWorld(GetWorld()); // Actor->GetWorld()->TimeSeconds ActorInfo->NextUpdateTime = TimeSeconds + 0.2f * FRand(); } } @@ -719,26 +841,39 @@ int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* // if it is relevant then mark the channel as relevant for a short amount of time if (bIsRelevant) { + // Channel->GetRelevantTime() = GetTime() - 1.0; // + 0.5f * SRand(); // scufed fn wtf Channel->GetRelevantTime() = GetTime() + 0.5f * SRand(); } - if (ReplicateActor(Channel)) + if (Channel->IsNetReady(0)) { - ActorUpdatesThisConnectionSent++; + if (ReplicateActor(Channel)) + { + ActorUpdatesThisConnectionSent++; - // Calculate min delta (max rate actor will upate), and max delta (slowest rate actor will update) - const float MinOptimalDelta = 1.0f / Actor->GetNetUpdateFrequency(); - const float MaxOptimalDelta = FMath::Max(1.0f / Actor->GetMinNetUpdateFrequency(), MinOptimalDelta); - const float DeltaBetweenReplications = (UGameplayStatics::GetTimeSeconds(GetWorld()) - ActorInfo->LastNetReplicateTime); + // Calculate min delta (max rate actor will upate), and max delta (slowest rate actor will update) + const float MinOptimalDelta = 1.0f / Actor->GetNetUpdateFrequency(); + const float MaxOptimalDelta = FMath::Max(1.0f / Actor->GetMinNetUpdateFrequency(), MinOptimalDelta); + const float DeltaBetweenReplications = (GetTimeSecondsForWorld(GetWorld()) - ActorInfo->LastNetReplicateTime); - // Choose an optimal time, we choose 70% of the actual rate to allow frequency to go up if needed - ActorInfo->OptimalNetUpdateDelta = FMath::Clamp(DeltaBetweenReplications * 0.7f, MinOptimalDelta, MaxOptimalDelta); - ActorInfo->LastNetReplicateTime = UGameplayStatics::GetTimeSeconds(GetWorld()); - ReplicatedActors.emplace(Actor->GetFullName()); + // Choose an optimal time, we choose 70% of the actual rate to allow frequency to go up if needed + ActorInfo->OptimalNetUpdateDelta = FMath::Clamp(DeltaBetweenReplications * 0.7f, MinOptimalDelta, MaxOptimalDelta); + ActorInfo->LastNetReplicateTime = GetTimeSecondsForWorld(GetWorld()); + // ReplicatedActors.emplace(Actor->GetFullName()); + } + + ActorUpdatesThisConnection++; + OutUpdated++; + } + else + { + Actor->ForceNetUpdate(); } - ActorUpdatesThisConnection++; - OutUpdated++; + if (!Connection->IsNetReady(0)) + { + return j; + } } } @@ -752,6 +887,8 @@ int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* } } + // LOG_INFO(LogDev, "FinalRelevantCount: {} ActorUpdatesThisConnection: {} ActorUpdatesThisConnectionSent: {}", FinalRelevantCount, ActorUpdatesThisConnection, ActorUpdatesThisConnectionSent); + return FinalSortedCount; } @@ -769,15 +906,17 @@ int32 UNetDriver::ServerReplicateActors() return 0; } - // AFortWorldSettings* WorldSettings = GetFortWorldSettings(NetDriver->World); + auto World = GetNetDriverWorld(); + + AWorldSettings* WorldSettings = World->GetWorldSettings(); // bool bCPUSaturated = false; - float ServerTickTime = 30.f; // Globals::MaxTickRate; // GEngine->GetMaxTickRate(DeltaSeconds); - /* if (ServerTickTime == 0.f) + float ServerTickTime = GetMaxTickRateHook(); + if (ServerTickTime == 0.f) { - ServerTickTime = DeltaSeconds; + // ServerTickTime = DeltaSeconds; } - else */ + else { ServerTickTime = 1.f / ServerTickTime; // bCPUSaturated = DeltaSeconds > 1.2f * ServerTickTime; @@ -789,9 +928,7 @@ int32 UNetDriver::ServerReplicateActors() // std::cout << "ConsiderList.size(): " << GetNetworkObjectList(NetDriver).ActiveNetworkObjects.Num() << '\n'; - auto World = GetWorld(); - - ServerReplicateActors_BuildConsiderList(ConsiderList); + ServerReplicateActors_BuildConsiderList(ConsiderList, ServerTickTime); bool bCPUSaturated = false; @@ -804,7 +941,19 @@ int32 UNetDriver::ServerReplicateActors() if (!Connection) continue; - // idk some dormancy validate stuff should go here + // net.DormancyValidate can be set to 2 to validate all dormant actors against last known state before going dormant + /* 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 this client shouldn't be ticked this frame if (i >= NumClientsToTick) @@ -819,18 +968,7 @@ int32 UNetDriver::ServerReplicateActors() { // find the channel - UActorChannel* Channel = nullptr; - - auto& ActorChannels = Connection->GetActorChannels(); - - for (int i = 0; i < ActorChannels.Pairs.Num(); i++) - { - if (ActorChannels.Pairs[i].First == ConsiderList[ConsiderIdx]->WeakActor) - { - Channel = ActorChannels.Pairs[i].Second; - break; - } - } + UActorChannel* Channel = FindChannel(Connection, ConsiderList[ConsiderIdx]); // and if the channel last update time doesn't match the last net update time for the actor if (Channel != NULL && Channel->GetLastUpdateTime() < ConsiderList[ConsiderIdx]->LastNetUpdateTime) @@ -840,7 +978,8 @@ int32 UNetDriver::ServerReplicateActors() } } - // Connection->TimeSensitive = false; // TODO Milxnor + static auto TimeSensitiveOffset = 0x241; + Connection->Get(TimeSensitiveOffset) = false; } else if (Connection->GetViewTarget()) { @@ -849,7 +988,6 @@ int32 UNetDriver::ServerReplicateActors() // ConnectionViewers.Reset(); std::vector ConnectionViewers; - // new(ConnectionViewers)FNetViewer(Connection, DeltaSeconds); ConnectionViewers.push_back(ConstructNetViewer(Connection)); // send ClientAdjustment if necessary @@ -899,24 +1037,24 @@ int32 UNetDriver::ServerReplicateActors() } } } + + ConnectionViewers.clear(); } } // shuffle the list of connections if not all connections were ticked - /* - if (NumClientsToTick < NetDriver->ClientConnections.Num()) + /* if (NumClientsToTick < 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); + UNetConnection* Connection = ClientConnections.at(0); + ClientConnections.RemoveAt(0, 1); + 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 994dd91..477738e 100644 --- a/Project Reboot 3.0/NetDriver.h +++ b/Project Reboot 3.0/NetDriver.h @@ -23,6 +23,7 @@ struct FActorDestructionInfo FString PathName; FName StreamingLevelName; }; + struct FNetworkObjectInfo { /** Pointer to the replicated actor. */ @@ -109,6 +110,16 @@ struct FURL // idk where this actually goes FString Portal; // 0x0058(0x0010) (ZeroConstructor) }; +struct FNetGUIDCache +{ + bool SupportsObject(const UObject* Object, const TWeakObjectPtr* WeakObjectPtr = nullptr) const + { + // 1.11 + bool (*SupportsObjectOriginal)(__int64, const UObject*, const TWeakObjectPtr*) = decltype(SupportsObjectOriginal)(__int64(GetModuleHandleW(0)) + 0x1AF01E0); + return SupportsObjectOriginal(__int64(this), Object, WeakObjectPtr); + } +}; + class UNetDriver : public UObject { public: @@ -119,6 +130,18 @@ public: static void TickFlushHook(UNetDriver* NetDriver); + FNetGUIDCache* GetGuidCache() + { + static auto GuidCacheOffset = GetOffset("WorldPackage") + 8; // checked for 1.11 + return GetPtr(GuidCacheOffset); + } + + UWorld*& GetNetDriverWorld() const + { + static auto WorldOffset = GetOffset("World"); + return Get(WorldOffset); + } + UObject*& GetWorldPackage() const { static auto WorldPackageOffset = GetOffset("WorldPackage"); @@ -161,7 +184,7 @@ public: 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); - void ServerReplicateActors_BuildConsiderList(std::vector& OutConsiderList); + 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); FNetworkObjectList& GetNetworkObjectList(); }; \ No newline at end of file diff --git a/Project Reboot 3.0/Object.h b/Project Reboot 3.0/Object.h index 0919423..dc36f75 100644 --- a/Project Reboot 3.0/Object.h +++ b/Project Reboot 3.0/Object.h @@ -31,7 +31,7 @@ public: FName NamePrivate; UObject* OuterPrivate; - static inline void (*ProcessEventOriginal)(UObject*, UFunction*, void*); + static inline void (*ProcessEventOriginal)(const UObject*, UFunction*, void*); /* virtual */ void ProcessEvent(UFunction* Function, void* Parms = nullptr) { @@ -39,6 +39,12 @@ public: ProcessEventOriginal(this, Function, Parms); } + /* virtual */ void ProcessEvent(UFunction* Function, void* Parms = nullptr) const + { + // LOG_INFO(LogDev, "PE: 0x{:x}", __int64(ProcessEventOriginal) - __int64(GetModuleHandleW(0))); + ProcessEventOriginal(this, Function, Parms); + } + std::string GetName() { return NamePrivate.ToString(); } std::string GetFullName(); diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj b/Project Reboot 3.0/Project Reboot 3.0.vcxproj index c26ea1c..e84d1c1 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj @@ -175,6 +175,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 34abdbe..19062c5 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters @@ -179,6 +179,9 @@ Engine\Source\Runtime\Engine\Private + + Engine\Source\Runtime\Engine\Private + diff --git a/Project Reboot 3.0/Set.h b/Project Reboot 3.0/Set.h index adc03f6..fcceb91 100644 --- a/Project Reboot 3.0/Set.h +++ b/Project Reboot 3.0/Set.h @@ -185,6 +185,7 @@ public: return It.GetIndex(); } } + return -1; } FORCEINLINE bool Remove(const SetType& ElementToRemove) diff --git a/Project Reboot 3.0/SparseArray.h b/Project Reboot 3.0/SparseArray.h index e707854..3f67759 100644 --- a/Project Reboot 3.0/SparseArray.h +++ b/Project Reboot 3.0/SparseArray.h @@ -2,6 +2,7 @@ #include "Array.h" #include "BitArray.h" +#include "log.h" template union TSparseArrayElementOrListLink @@ -161,11 +162,17 @@ public: 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; diff --git a/Project Reboot 3.0/UnrealString.h b/Project Reboot 3.0/UnrealString.h index fc36d82..6422597 100644 --- a/Project Reboot 3.0/UnrealString.h +++ b/Project Reboot 3.0/UnrealString.h @@ -7,6 +7,7 @@ class FString { +public: TArray Data; public: diff --git a/Project Reboot 3.0/WeakObjectPtr.h b/Project Reboot 3.0/WeakObjectPtr.h index ba3ca84..7496a5a 100644 --- a/Project Reboot 3.0/WeakObjectPtr.h +++ b/Project Reboot 3.0/WeakObjectPtr.h @@ -15,6 +15,6 @@ public: bool operator==(const FWeakObjectPtr& other) { - return ObjectIndex == other.ObjectIndex && ObjectSerialNumber == other.ObjectSerialNumber; // i need to check in ue if we check serialnumber + return ObjectIndex == other.ObjectIndex && ObjectSerialNumber == other.ObjectSerialNumber; } }; \ No newline at end of file diff --git a/Project Reboot 3.0/World.cpp b/Project Reboot 3.0/World.cpp index 28d4343..f6a5f66 100644 --- a/Project Reboot 3.0/World.cpp +++ b/Project Reboot 3.0/World.cpp @@ -5,10 +5,10 @@ #include "reboot.h" -UObject* UWorld::K2_GetWorldSettings() +AWorldSettings* UWorld::K2_GetWorldSettings() { static auto fn = FindObject("/Script/Engine.World.K2_GetWorldSettings"); - UObject* WorldSettings; + AWorldSettings* WorldSettings; this->ProcessEvent(fn, &WorldSettings); return WorldSettings; } @@ -80,4 +80,36 @@ void UWorld::Listen() *(UNetDriver**)(__int64(LevelCollections.AtPtr(1, LevelCollectionSize)) + 0x10) = NewNetDriver; LOG_INFO(LogNet, "Listening on port {}!", Port + AmountOfRestarts); +} + +AWorldSettings* UWorld::GetWorldSettings(const bool bCheckStreamingPersistent, const bool bChecked) const +{ + // checkSlow(!IsInActualRenderingThread()); + AWorldSettings* WorldSettings = nullptr; + static auto PersistentLevelOffset = GetOffset("PersistentLevel"); + if (Get(PersistentLevelOffset)) + { + WorldSettings = Get(PersistentLevelOffset)->GetWorldSettings(bChecked); + + if (bCheckStreamingPersistent) + { + static auto StreamingLevelsOffset = GetOffset("StreamingLevels"); + auto& StreamingLevels = Get>(StreamingLevelsOffset); + + static auto LevelStreamingPersistentClass = FindObject("/Script/Engine.LevelStreamingPersistent"); + + if (StreamingLevels.Num() > 0 && + StreamingLevels.at(0) && + StreamingLevels.at(0)->IsA(LevelStreamingPersistentClass)) + { + static auto LoadedLevelOffset = StreamingLevels.at(0)->GetOffset("LoadedLevel"); + ULevel* Level = StreamingLevels.at(0)->Get(LoadedLevelOffset); + if (Level != nullptr) + { + WorldSettings = Level->GetWorldSettings(); + } + } + } + } + return WorldSettings; } \ No newline at end of file diff --git a/Project Reboot 3.0/World.h b/Project Reboot 3.0/World.h index efd6c65..4b44c82 100644 --- a/Project Reboot 3.0/World.h +++ b/Project Reboot 3.0/World.h @@ -4,12 +4,18 @@ #include "Transform.h" #include "Object.h" #include "Rotator.h" +#include "Actor.h" struct FNetworkNotify { }; +class AWorldSettings : public AActor +{ +public: +}; + struct FActorSpawnParameters { FName Name; @@ -56,7 +62,8 @@ public: return SpawnActor(Class, UserTransformPtr, SpawnParameters); } - UObject* K2_GetWorldSettings(); + AWorldSettings* GetWorldSettings(bool bCheckStreamingPersistent = false, bool bChecked = true) const; + AWorldSettings* K2_GetWorldSettings(); void Listen(); }; \ No newline at end of file diff --git a/Project Reboot 3.0/dllmain.cpp b/Project Reboot 3.0/dllmain.cpp index ff081fe..aafb804 100644 --- a/Project Reboot 3.0/dllmain.cpp +++ b/Project Reboot 3.0/dllmain.cpp @@ -44,7 +44,6 @@ static ENetMode GetNetModeHook() { return NetMode; } static ENetMode GetNetModeHook2() { return NetMode; } static bool ReturnTrueHook() { return true; } -static float GetMaxTickRateHook() { return 30.f; } static int Return2Hook() { return 2; } static void NoMCPHook() { return; } @@ -133,6 +132,9 @@ DWORD WINAPI Main(LPVOID) static auto AthenaMarkerComponentDefault = FindObject(L"/Script/FortniteGame.Default__AthenaMarkerComponent"); static auto FortWeaponDefault = FindObject(L"/Script/FortniteGame.Default__FortWeapon"); + // UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), L"log LogNetPackageMap VeryVerbose", nullptr); + // UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), L"log LogNetTraffic VeryVerbose", nullptr); + // UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), L"log LogNet VeryVerbose", nullptr); UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), L"log LogBuilding VeryVerbose", nullptr); // UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), L"log LogFortUIDirector NoLogging", nullptr); UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), L"log LogAbilitySystem VeryVerbose", nullptr); @@ -242,7 +244,14 @@ DWORD WINAPI Main(LPVOID) } } - for (auto func : Addresses::GetFunctionsToNull()) + auto AddressesToNull = Addresses::GetFunctionsToNull(); + + auto ServerCheatAllIndex = GetFunctionIdxOrPtr(FindObject("/Script/FortniteGame.FortPlayerController.ServerCheatAll")); + + if (ServerCheatAllIndex) + AddressesToNull.push_back(FortPlayerControllerAthenaDefault->VFTable[ServerCheatAllIndex / 8]); + + for (auto func : AddressesToNull) { if (func == 0) continue; diff --git a/Project Reboot 3.0/finder.h b/Project Reboot 3.0/finder.h index 5a17c60..2acb7c8 100644 --- a/Project Reboot 3.0/finder.h +++ b/Project Reboot 3.0/finder.h @@ -974,7 +974,7 @@ static inline uint64 FindGIsClient() // return __int64(GetModuleHandleW(0)) + 0x46AD734; if (Fortnite_Version == 1.72) return __int64(GetModuleHandleW(0)) + 0x6536B65; - if (Fortnite_Version == 1.8) + if (Fortnite_Version == 1.8) return __int64(GetModuleHandleW(0)) + 0x66637E5; if (Fortnite_Version == 1.11) return __int64(GetModuleHandleW(0)) + 0x5BAA38F; @@ -1125,8 +1125,11 @@ static inline uint64 FindPickTeam() { if (Engine_Version == 426) { - auto testAddr = Memcury::Scanner::FindPattern("88 54 24 10 53 56 41 54 41 55 41 56 48 83 EC 60 4C 8B A1").Get(); // 14.60 what is happening lol ???? + auto testAddr = Memcury::Scanner::FindPattern("88 54 24 10 53 56 41 54 41 55 41 56 48 83 EC 60 4C 8B A1", false).Get(); // 14.60 what is happening lol ???? + if (!testAddr) + testAddr = Memcury::Scanner::FindPattern("88 54 24 10 53 55 56 41 55 41 56 48 83 EC 70 48 8B", false).Get(); // 15.10 + if (testAddr) return testAddr; } @@ -1137,6 +1140,9 @@ static inline uint64 FindPickTeam() else if (Engine_Version >= 427) // different start return Memcury::Scanner::FindPattern("48 89 5C 24 ? 88 54 24 10 55 56 57 41 54 41 55 41 56 41 57 48 8B EC 48 83 EC 70 4C 8B A1").Get(); + if (Fortnite_Version == 7.20) + return Memcury::Scanner::FindPattern("89 54 24 10 53 56 41 54 41 55 41 56 48 81 EC").Get(); + auto Addr = Memcury::Scanner::FindStringRef(L"PickTeam for [%s] used beacon value [%d]", false, 0, Engine_Version >= 427); // todo check if its just s18+ but this doesn't matter for now cuz we hardcode sig if (!Addr.Get()) diff --git a/Project Reboot 3.0/hooking.h b/Project Reboot 3.0/hooking.h index ba6cb55..8708976 100644 --- a/Project Reboot 3.0/hooking.h +++ b/Project Reboot 3.0/hooking.h @@ -111,6 +111,9 @@ inline __int64 GetIndexFromVirtualFunctionCall(__int64 NativeAddr) inline __int64 GetFunctionIdxOrPtr(UFunction* Function) { + if (!Function) + return 0; + auto NativeAddr = __int64(Function->GetFunc()); auto FuncName = Function->GetName(); diff --git a/Project Reboot 3.0/reboot.h b/Project Reboot 3.0/reboot.h index dfd210e..c7ab0a1 100644 --- a/Project Reboot 3.0/reboot.h +++ b/Project Reboot 3.0/reboot.h @@ -306,4 +306,8 @@ namespace MemberOffsets { extern inline int bDBNO = 0, Downer = 0, FinisherOrDowner = 0, DeathCause = 0, Distance = 0, DeathLocation = 0, bInitialized = 0, DeathTags = 0; } -} \ No newline at end of file +} + +static inline float GetMaxTickRateHook() { return 30.f; } + +#define VALIDATEOFFSET(offset) if (!offset) LOG_WARN(LogDev, "[{}] Invalid offset", __FUNCTIONNAME__); \ No newline at end of file