diff --git a/Project Reboot 3.0/AthenaBarrierFlag.h b/Project Reboot 3.0/AthenaBarrierFlag.h new file mode 100644 index 0000000..66bf48a --- /dev/null +++ b/Project Reboot 3.0/AthenaBarrierFlag.h @@ -0,0 +1,44 @@ +#pragma once + +#include "BuildingGameplayActor.h" +#include "AthenaBarrierObjective.h" + +struct FBarrierFlagDisplayData +{ + static UStruct* GetStruct() + { + static auto Struct = FindObject("/Script/FortniteGame.BarrierFlagDisplayData"); + return Struct; + } + + static int GetStructSize() { return GetStruct()->GetPropertiesSize(); } + + UStaticMesh* GetHeadMesh() + { + static auto HeadMeshOffset = FindOffsetStruct("/Script/FortniteGame.BarrierFlagDisplayData", "HeadMesh"); + return *(UStaticMesh**)(__int64(this) + HeadMeshOffset); + } + + FVector& GetMeshScale() + { + static auto MeshScaleOffset = FindOffsetStruct("/Script/FortniteGame.BarrierFlagDisplayData", "MeshScale"); + return *(FVector*)(__int64(this) + MeshScaleOffset); + } +}; + +class AAthenaBarrierFlag : public ABuildingGameplayActor +{ +public: + EBarrierFoodTeam& GetFoodTeam() + { + static auto FoodTeamOffset = GetOffset("FoodTeam"); + return Get(FoodTeamOffset); + } + + FBarrierFlagDisplayData* GetDisplayData(EBarrierFoodTeam FoodTeam) + { + static auto FoodDisplayDataOffset = GetOffset("FoodDisplayData"); + auto FoodDisplayData = Get(FoodDisplayDataOffset); // Array of size 2 + return &FoodDisplayData[(int)FoodTeam]; + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/AthenaBarrierObjective.h b/Project Reboot 3.0/AthenaBarrierObjective.h new file mode 100644 index 0000000..d20e4b7 --- /dev/null +++ b/Project Reboot 3.0/AthenaBarrierObjective.h @@ -0,0 +1,59 @@ +#pragma once + +#include "BuildingGameplayActor.h" + +using UStaticMesh = UObject; +using UMaterialInterface = UObject; + +enum class EBarrierFoodTeam : uint8_t +{ + Burger = 0, + Tomato = 1, + MAX = 2 +}; + +struct FBarrierObjectiveDisplayData +{ + UStaticMesh* HeadMesh; // 0x0000(0x0008) (Edit, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + FVector MeshScale; // 0x0008(0x000C) (Edit, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + FVector MeshRelativeOffset; // 0x0014(0x000C) (Edit, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + TArray MaterialsToSwap; // 0x0020(0x0010) (Edit, ZeroConstructor, NativeAccessSpecifierPublic) +}; + +class AAthenaBarrierObjective : public ABuildingGameplayActor +{ +public: + void SetHeadMesh(UStaticMesh* NewMesh, const FVector& NewScale, const FVector& NewOffset, const TArray& MaterialsToSwap) + { + static auto SetHeadMeshFn = FindObject("/Script/FortniteGame.AthenaBarrierObjective.SetHeadMesh"); + + struct + { + UStaticMesh* NewMesh; // (Parm, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + FVector NewScale; // (Parm, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + FVector NewOffset; // (Parm, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + TArray MaterialsToSwap; // (ConstParm, Parm, OutParm, ZeroConstructor, ReferenceParm, NativeAccessSpecifierPublic) + } AAthenaBarrierObjective_SetHeadMesh_Params{ NewMesh, NewScale, NewOffset, MaterialsToSwap }; + + this->ProcessEvent(SetHeadMeshFn, &AAthenaBarrierObjective_SetHeadMesh_Params); + } + + EBarrierFoodTeam& GetFoodTeam() + { + static auto FoodTeamOffset = GetOffset("FoodTeam"); + return Get(FoodTeamOffset); + } + + FBarrierObjectiveDisplayData* GetFoodDisplayData(EBarrierFoodTeam FoodTeam) + { + static auto FoodDisplayDataOffset = GetOffset("FoodDisplayData"); + auto FoodDisplayData = Get(FoodDisplayDataOffset); // Array size of 2 + return &FoodDisplayData[(int)FoodTeam]; + } + + static UClass* StaticClass() + { + static auto Class = FindObject("/Script/FortniteGame.AthenaBarrierObjective"); + return Class; + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/AthenaBigBaseWall.h b/Project Reboot 3.0/AthenaBigBaseWall.h new file mode 100644 index 0000000..3195e6b --- /dev/null +++ b/Project Reboot 3.0/AthenaBigBaseWall.h @@ -0,0 +1,8 @@ +#pragma once + +#include "BuildingGameplayActor.h" + +class AAthenaBigBaseWall : public ABuildingGameplayActor +{ +public: +}; \ No newline at end of file diff --git a/Project Reboot 3.0/Class.cpp b/Project Reboot 3.0/Class.cpp index a58a83f..47b738a 100644 --- a/Project Reboot 3.0/Class.cpp +++ b/Project Reboot 3.0/Class.cpp @@ -9,6 +9,9 @@ UObject* UClass::CreateDefaultObject() auto name = this->GetFullName(); + if (name.contains("Default__")) + return this; + auto defaultafqaf = defaultAbilities.find(name); UObject* DefaultObject = nullptr; diff --git a/Project Reboot 3.0/Class.h b/Project Reboot 3.0/Class.h index 060a16a..094414f 100644 --- a/Project Reboot 3.0/Class.h +++ b/Project Reboot 3.0/Class.h @@ -20,6 +20,7 @@ class UClass : public UStruct { public: UObject* CreateDefaultObject(); + UClass* GetSuperStruct() { return *(UClass**)(__int64(this) + Offsets::SuperStruct); } // idk if this is in UStruct }; class UFunction : public UStruct diff --git a/Project Reboot 3.0/Delegate.h b/Project Reboot 3.0/Delegate.h index 3f59c93..525577a 100644 --- a/Project Reboot 3.0/Delegate.h +++ b/Project Reboot 3.0/Delegate.h @@ -1,2 +1,19 @@ #pragma once +#include "DelegateBase.h" +#include "DelegateSignatureImpl.inl" + +#define FUNC_CONCAT( ... ) __VA_ARGS__ + +#define FUNC_DECLARE_DELEGATE( DelegateName, ReturnType, ... ) \ + typedef TBaseDelegate/*<__VA_ARGS__>*/ DelegateName; + +#define FUNC_DECLARE_DYNAMIC_DELEGATE( TWeakPtr, DynamicDelegateName, ExecFunction, FuncParamList, FuncParamPassThru, ... ) \ + class DynamicDelegateName : public TBaseDynamicDelegate \ + { \ + public: \ + DynamicDelegateName() \ + { \ + } \ + \ + }; \ No newline at end of file diff --git a/Project Reboot 3.0/DelegateBase.h b/Project Reboot 3.0/DelegateBase.h index 6ba0a21..f657f0e 100644 --- a/Project Reboot 3.0/DelegateBase.h +++ b/Project Reboot 3.0/DelegateBase.h @@ -2,19 +2,27 @@ #include "inc.h" +#include "TypeCompatibleBytes.h" +#include "ContainerAllocationPolicies.h" -/* +#if !defined(_WIN32) || defined(_WIN64) +// Let delegates store up to 32 bytes which are 16-byte aligned before we heap allocate typedef TAlignedBytes<16, 16> FAlignedInlineDelegateType; #if USE_SMALL_DELEGATES typedef FHeapAllocator FDelegateAllocatorType; #else typedef TInlineAllocator<2> FDelegateAllocatorType; #endif -*/ +#else +// ... except on Win32, because we can't pass 16-byte aligned types by value, as some delegates are +// so we'll just keep it heap-allocated, which are always sufficiently aligned. +typedef TAlignedBytes<16, 8> FAlignedInlineDelegateType; +typedef FHeapAllocator FDelegateAllocatorType; +#endif class FDelegateBase { public: - // FDelegateAllocatorType::ForElementType DelegateAllocator; - // int32 DelegateSize; + FDelegateAllocatorType::ForElementType DelegateAllocator; + int32 DelegateSize; }; \ No newline at end of file diff --git a/Project Reboot 3.0/DelegateCombinations.h b/Project Reboot 3.0/DelegateCombinations.h new file mode 100644 index 0000000..4a28bbe --- /dev/null +++ b/Project Reboot 3.0/DelegateCombinations.h @@ -0,0 +1,7 @@ +#pragma once + +#include "Delegate.h" +#include "ObjectMacros.h" + +#define DECLARE_DELEGATE( DelegateName ) FUNC_DECLARE_DELEGATE( DelegateName, void ) +#define DECLARE_DYNAMIC_DELEGATE( DelegateName ) /* BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE) */ FUNC_DECLARE_DYNAMIC_DELEGATE( FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, , FUNC_CONCAT( *this ), void ) diff --git a/Project Reboot 3.0/DelegateInstanceInterface.h b/Project Reboot 3.0/DelegateInstanceInterface.h new file mode 100644 index 0000000..a9c7f29 --- /dev/null +++ b/Project Reboot 3.0/DelegateInstanceInterface.h @@ -0,0 +1,16 @@ +#pragma once + +template +struct TMemFunPtrType; + +template +struct TMemFunPtrType +{ + typedef RetType(Class::* Type)(ArgTypes...); +}; + +template +struct TMemFunPtrType +{ + typedef RetType(Class::* Type)(ArgTypes...) const; +}; \ No newline at end of file diff --git a/Project Reboot 3.0/DelegateSignatureImpl.inl b/Project Reboot 3.0/DelegateSignatureImpl.inl index 06c01cc..f04ec82 100644 --- a/Project Reboot 3.0/DelegateSignatureImpl.inl +++ b/Project Reboot 3.0/DelegateSignatureImpl.inl @@ -1,9 +1,63 @@ #pragma once #include "DelegateBase.h" +#include "ScriptDelegates.h" -template +#include "DelegateInstanceInterface.h" +#include "RemoveReference.h" +#include "TypeWrapper.h" + +// template // (Milxnor) IDK IM SCUFFED class TBaseDelegate : public FDelegateBase { public: + // (Milxnor) YEAH NO + + /* + typedef typename TUnwrapType::Type RetValType; + + template + FUNCTION_CHECK_RETURN_START + inline static TBaseDelegate CreateRaw(UserClass* InUserObject, typename TMemFunPtrType::Type InFunc, VarTypes... Vars) + FUNCTION_CHECK_RETURN_END + { + // UE_STATIC_DEPRECATE(4.23, TIsConst::Value, "Binding a delegate with a const object pointer and non-const function is deprecated."); + + TBaseDelegate Result; + TBaseRawMethodDelegateInstance::Create(Result, InUserObject, InFunc, Vars...); + return Result; + } + template + FUNCTION_CHECK_RETURN_START + inline static TBaseDelegate CreateRaw(UserClass* InUserObject, typename TMemFunPtrType::Type InFunc, VarTypes... Vars) + FUNCTION_CHECK_RETURN_END + { + TBaseDelegate Result; + TBaseRawMethodDelegateInstance::Create(Result, InUserObject, InFunc, Vars...); + return Result; + } + + + template + inline void BindRaw(UserClass* InUserObject, typename TMemFunPtrType::Type InFunc, VarTypes... Vars) + { + // UE_STATIC_DEPRECATE(4.23, TIsConst::Value, "Binding a delegate with a const object pointer and non-const function is deprecated."); + + *this = CreateRaw(const_cast::Type*>(InUserObject), InFunc, Vars...); + } + template + inline void BindRaw(UserClass* InUserObject, typename TMemFunPtrType::Type InFunc, VarTypes... Vars) + { + *this = CreateRaw(InUserObject, InFunc, Vars...); + } */ +}; + +template +class TBaseDynamicDelegate : public TScriptDelegate +{ +public: + /** + * Default constructor + */ + TBaseDynamicDelegate() { } }; \ No newline at end of file diff --git a/Project Reboot 3.0/EngineTypes.h b/Project Reboot 3.0/EngineTypes.h index eed5c0d..2566be8 100644 --- a/Project Reboot 3.0/EngineTypes.h +++ b/Project Reboot 3.0/EngineTypes.h @@ -2,6 +2,8 @@ #include "Object.h" +#include "DelegateCombinations.h" + enum class ESpawnActorCollisionHandlingMethod : uint8 { Undefined, @@ -15,4 +17,74 @@ struct FHitResult { static class UStruct* GetStruct(); static int GetStructSize(); -}; \ No newline at end of file +}; + +struct FTimerHandle +{ + FTimerHandle() + : Handle(0) + { + } + + /** True if this handle was ever initialized by the timer manager */ + bool IsValid() const + { + return Handle != 0; + } + + /** Explicitly clear handle */ + void Invalidate() + { + Handle = 0; + } + + bool operator==(const FTimerHandle& Other) const + { + return Handle == Other.Handle; + } + + bool operator!=(const FTimerHandle& Other) const + { + return Handle != Other.Handle; + } + + /* FString ToString() const + { + return FString::Printf(TEXT("%llu"), Handle); + } */ + +// private: + static const uint32 IndexBits = 24; + static const uint32 SerialNumberBits = 40; + + static_assert(IndexBits + SerialNumberBits == 64, "The space for the timer index and serial number should total 64 bits"); + + static const int32 MaxIndex = (int32)1 << IndexBits; + static const uint64 MaxSerialNumber = (uint64)1 << SerialNumberBits; + + void SetIndexAndSerialNumber(int32 Index, uint64 SerialNumber) + { + // check(Index >= 0 && Index < MaxIndex); + // check(SerialNumber < MaxSerialNumber); + Handle = (SerialNumber << IndexBits) | (uint64)(uint32)Index; + } + + FORCEINLINE int32 GetIndex() const + { + return (int32)(Handle & (uint64)(MaxIndex - 1)); + } + + FORCEINLINE uint64 GetSerialNumber() const + { + return Handle >> IndexBits; + } + + uint64 Handle; + + /* friend uint32 GetTypeHash(const FTimerHandle& InHandle) + { + return GetTypeHash(InHandle.Handle); + } */ +}; + +DECLARE_DYNAMIC_DELEGATE(FTimerDynamicDelegate); \ No newline at end of file diff --git a/Project Reboot 3.0/ExpressionParserTypes.inl b/Project Reboot 3.0/ExpressionParserTypes.inl new file mode 100644 index 0000000..7b9637e --- /dev/null +++ b/Project Reboot 3.0/ExpressionParserTypes.inl @@ -0,0 +1 @@ +#pragma once \ No newline at end of file diff --git a/Project Reboot 3.0/FortAthenaMutator.h b/Project Reboot 3.0/FortAthenaMutator.h index be3aabd..f027956 100644 --- a/Project Reboot 3.0/FortAthenaMutator.h +++ b/Project Reboot 3.0/FortAthenaMutator.h @@ -1,8 +1,33 @@ #pragma once +#include + #include "Actor.h" +#include "Stack.h" + +#include "ScriptInterface.h" +#include "FortGameStateAthena.h" +#include "GameplayStatics.h" + class AFortAthenaMutator : public AActor // AFortGameplayMutator { public: -}; \ No newline at end of file + static UClass* StaticClass() + { + static auto Class = FindObject("/Script/FortniteGame.FortAthenaMutator"); + return Class; + } +}; + +static inline void LoopMutators(std::function Callback) +{ + auto AllMutators = UGameplayStatics::GetAllActorsOfClass(GetWorld(), AFortAthenaMutator::StaticClass()); + + for (int i = 0; i < AllMutators.Num(); i++) + { + Callback((AFortAthenaMutator*)AllMutators.at(i)); + } + + AllMutators.Free(); +} \ No newline at end of file diff --git a/Project Reboot 3.0/FortAthenaMutator_Barrier.cpp b/Project Reboot 3.0/FortAthenaMutator_Barrier.cpp new file mode 100644 index 0000000..3501da4 --- /dev/null +++ b/Project Reboot 3.0/FortAthenaMutator_Barrier.cpp @@ -0,0 +1,30 @@ +#include "FortAthenaMutator_Barrier.h" + +void AFortAthenaMutator_Barrier::OnGamePhaseStepChangedHook(UObject* Context, FFrame& Stack, void* Ret) +{ + /* + TScriptInterface SafeZoneInterface; + EAthenaGamePhaseStep GamePhaseStep; + + static auto SafeZoneInterfaceOffset = FindOffsetStruct("/Script/FortniteGame.FortAthenaMutator_Barrier.OnGamePhaseStepChanged", "SafeZoneInterface", false); + + if (SafeZoneInterfaceOffset != -1) + Stack.StepCompiledIn(&SafeZoneInterface); + + Stack.StepCompiledIn(&GamePhaseStep); + + LOG_INFO(LogDev, "{} GamePhaseStep: {}", __FUNCTION__, (int)GamePhaseStep); + + if (GamePhaseStep == EAthenaGamePhaseStep::Warmup) + { + // idk when they spawn the barrier could also be warmup or something + } + else if (GamePhaseStep == EAthenaGamePhaseStep::BusLocked) + { + // idk if they spawn the heads on flying or locked + } + + */ + + return OnGamePhaseStepChangedOriginal(Context, Stack, Ret); +} \ No newline at end of file diff --git a/Project Reboot 3.0/FortAthenaMutator_Barrier.h b/Project Reboot 3.0/FortAthenaMutator_Barrier.h new file mode 100644 index 0000000..7756bb6 --- /dev/null +++ b/Project Reboot 3.0/FortAthenaMutator_Barrier.h @@ -0,0 +1,73 @@ +#pragma once + +#include "FortAthenaMutator.h" +#include "AthenaBigBaseWall.h" +#include "AthenaBarrierObjective.h" +#include "AthenaBarrierFlag.h" + +struct FBarrierTeamState // Idk if this actually changes +{ + static UStruct* GetStruct() + { + static auto Struct = FindObject("/Script/FortniteGame.BarrierTeamState"); + return Struct; + } + + static int GetStructSize() { return GetStruct()->GetPropertiesSize(); } + + EBarrierFoodTeam& GetFoodTeam() + { + static auto FoodTeamOffset = FindOffsetStruct("/Script/FortniteGame.BarrierTeamState", "FoodTeam"); + return *(EBarrierFoodTeam*)(__int64(this) + FoodTeamOffset); + } + + AAthenaBarrierFlag*& GetObjectiveFlag() + { + static auto ObjectiveFlagOffset = FindOffsetStruct("/Script/FortniteGame.BarrierTeamState", "ObjectiveFlag"); + return *(AAthenaBarrierFlag**)(__int64(this) + ObjectiveFlagOffset); + } + + AAthenaBarrierObjective*& GetObjectiveObject() + { + static auto ObjectiveObjectOffset = FindOffsetStruct("/Script/FortniteGame.BarrierTeamState", "ObjectiveObject"); + return *(AAthenaBarrierObjective**)(__int64(this) + ObjectiveObjectOffset); + } +}; + +class AFortAthenaMutator_Barrier : public AFortAthenaMutator +{ +public: + static inline void (*OnGamePhaseStepChangedOriginal)(UObject* Context, FFrame& Stack, void* Ret); + + UClass* GetBigBaseWallClass() + { + static auto BigBaseWallClassOffset = GetOffset("BigBaseWallClass"); + return Get(BigBaseWallClassOffset); + } + + UClass* GetObjectiveFlagClass() + { + static auto ObjectiveFlagOffset = GetOffset("ObjectiveFlag"); + return Get(ObjectiveFlagOffset); + } + + AAthenaBigBaseWall*& GetBigBaseWall() + { + static auto BigBaseWallOffset = GetOffset("BigBaseWall"); + return Get(BigBaseWallOffset); + } + + FBarrierTeamState* GetTeam_0_State() + { + static auto Team_0_StateOffset = GetOffset("Team_0_State"); + return GetPtr(Team_0_StateOffset); + } + + FBarrierTeamState* GetTeam_1_State() + { + static auto Team_1_StateOffset = GetOffset("Team_1_State"); + return GetPtr(Team_1_StateOffset); + } + + static void OnGamePhaseStepChangedHook(UObject* Context, FFrame& Stack, void* Ret); +}; \ No newline at end of file diff --git a/Project Reboot 3.0/FortAthenaMutator_Disco.cpp b/Project Reboot 3.0/FortAthenaMutator_Disco.cpp new file mode 100644 index 0000000..7982704 --- /dev/null +++ b/Project Reboot 3.0/FortAthenaMutator_Disco.cpp @@ -0,0 +1,18 @@ +#include "FortAthenaMutator_Disco.h" + +void AFortAthenaMutator_Disco::OnGamePhaseStepChangedHook(UObject* Context, FFrame& Stack, void* Ret) +{ + TScriptInterface SafeZoneInterface; + EAthenaGamePhaseStep GamePhaseStep = EAthenaGamePhaseStep::BusFlying; + + static auto SafeZoneInterfaceOffset = FindOffsetStruct("/Script/FortniteGame.FortAthenaMutator_Disco.OnGamePhaseStepChanged", "SafeZoneInterface", false); + + if (SafeZoneInterfaceOffset != -1) + Stack.StepCompiledIn(&SafeZoneInterface); + + // Stack.StepCompiledIn(&GamePhaseStep, true); + + LOG_INFO(LogDev, "{} GamePhaseStep: {}", __FUNCTION__, (int)GamePhaseStep); + + return OnGamePhaseStepChangedOriginal(Context, Stack, Ret); +} \ No newline at end of file diff --git a/Project Reboot 3.0/FortAthenaMutator_Disco.h b/Project Reboot 3.0/FortAthenaMutator_Disco.h new file mode 100644 index 0000000..90acf66 --- /dev/null +++ b/Project Reboot 3.0/FortAthenaMutator_Disco.h @@ -0,0 +1,29 @@ +#pragma once + +#include "FortAthenaMutator.h" +#include "Array.h" + +struct FControlPointSpawnData +{ + +}; + +class AFortAthenaMutator_Disco : public AFortAthenaMutator +{ +public: + static inline void (*OnGamePhaseStepChangedOriginal)(UObject* Context, FFrame& Stack, void* Ret); + + TArray& GetControlPointSpawnData() + { + static auto ControlPointSpawnDataOffset = GetOffset("ControlPointSpawnData"); + return Get>(ControlPointSpawnDataOffset); + } + + static UClass* StaticClass() + { + static auto Class = FindObject("/Script/FortniteGame.FortAthenaMutator_Disco"); + return Class; + } + + static void OnGamePhaseStepChangedHook(UObject* Context, FFrame& Stack, void* Ret); +}; \ No newline at end of file diff --git a/Project Reboot 3.0/FortAthenaMutator_GG.h b/Project Reboot 3.0/FortAthenaMutator_GG.h index b329f46..2b16c09 100644 --- a/Project Reboot 3.0/FortAthenaMutator_GG.h +++ b/Project Reboot 3.0/FortAthenaMutator_GG.h @@ -10,19 +10,41 @@ struct FGunGameGunEntry { - UFortWeaponItemDefinition* Weapon; // 0x0000(0x0008) (Edit, ZeroConstructor, DisableEditOnInstance, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) - FScalableFloat Enabled; // 0x0008(0x0020) (Edit, DisableEditOnInstance, NativeAccessSpecifierPublic) - FScalableFloat AwardAtElim; // 0x0028(0x0020) (Edit, DisableEditOnInstance, NativeAccessSpecifierPublic) + static UStruct* GetStruct() + { + static auto Struct = FindObject("/Script/FortniteGame.GunGameGunEntry"); + return Struct; + } + + static int GetStructSize() { return GetStruct()->GetPropertiesSize(); } + + UFortWeaponItemDefinition*& GetWeapon() + { + static auto WeaponOffset = FindOffsetStruct("/Script/FortniteGame.GunGameGunEntry", "Weapon"); + return *(UFortWeaponItemDefinition**)(__int64(this) + WeaponOffset); + } + + FScalableFloat& GetEnabled() + { + static auto EnabledOffset = FindOffsetStruct("/Script/FortniteGame.GunGameGunEntry", "Enabled"); + return *(FScalableFloat*)(__int64(this) + EnabledOffset); + } + + FScalableFloat& GetAwardAtElim() + { + static auto AwardAtElimOffset = FindOffsetStruct("/Script/FortniteGame.GunGameGunEntry", "AwardAtElim"); + return *(FScalableFloat*)(__int64(this) + AwardAtElimOffset); + } }; struct FGunGameGunEntries { - TArray Entries; // 0x0000(0x0010) (ZeroConstructor, NativeAccessSpecifierPublic) + TArray Entries; // 0x0000(0x0010) (ZeroConstructor, NativeAccessSpecifierPublic) }; struct FGunGamePlayerData { - TArray CurrentlyAssignedWeapons; // 0x0000(0x0010) (ZeroConstructor, NativeAccessSpecifierPublic) + TArray CurrentlyAssignedWeapons; // 0x0000(0x0010) (ZeroConstructor, NativeAccessSpecifierPublic) }; class AFortAthenaMutator_GG : public AFortAthenaMutator @@ -50,7 +72,7 @@ public: { auto& AwardEntriesAtElimMap = GetAwardEntriesAtElimMap(); - float Value = 0; + float Value = 0; // TODO Get from AwardAtElim for (auto& AwardEntry : AwardEntriesAtElimMap) { diff --git a/Project Reboot 3.0/FortAthenaMutator_GiveItemsAtGamePhaseStep.cpp b/Project Reboot 3.0/FortAthenaMutator_GiveItemsAtGamePhaseStep.cpp index 68985b5..f5a1fd5 100644 --- a/Project Reboot 3.0/FortAthenaMutator_GiveItemsAtGamePhaseStep.cpp +++ b/Project Reboot 3.0/FortAthenaMutator_GiveItemsAtGamePhaseStep.cpp @@ -2,7 +2,7 @@ void AFortAthenaMutator_GiveItemsAtGamePhaseStep::OnGamePhaseStepChangedHook(UObject* Context, FFrame& Stack, void* Ret) { - LOG_INFO(LogDev, "OnGamePhaseStepChangedHook!"); + LOG_INFO(LogDev, __FUNCTION__); return OnGamePhaseStepChangedOriginal(Context, Stack, Ret); } \ No newline at end of file diff --git a/Project Reboot 3.0/FortAthenaMutator_GiveItemsAtGamePhaseStep.h b/Project Reboot 3.0/FortAthenaMutator_GiveItemsAtGamePhaseStep.h index 805093e..067a78b 100644 --- a/Project Reboot 3.0/FortAthenaMutator_GiveItemsAtGamePhaseStep.h +++ b/Project Reboot 3.0/FortAthenaMutator_GiveItemsAtGamePhaseStep.h @@ -9,8 +9,25 @@ struct FItemsToGive { - UFortWorldItemDefinition* ItemToDrop; // 0x0000(0x0008) (Edit, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) - FScalableFloat NumberToGive; // 0x0008(0x0020) (Edit, NativeAccessSpecifierPublic) + static UStruct* GetStruct() + { + static auto Struct = FindObject("/Script/FortniteGame.ItemsToGive"); + return Struct; + } + + static int GetStructSize() { return GetStruct()->GetPropertiesSize(); } + + UFortWorldItemDefinition*& GetItemToDrop() + { + static auto ItemToDropOffset = FindOffsetStruct("/Script/FortniteGame.ItemsToGive", "ItemToDrop"); + return *(UFortWorldItemDefinition**)(__int64(this) + ItemToDropOffset); + } + + FScalableFloat& GetNumberToGive() + { + static auto NumberToGiveOffset = FindOffsetStruct("/Script/FortniteGame.ItemsToGive", "NumberToGive"); + return *(FScalableFloat*)(__int64(this) + NumberToGiveOffset); + } }; class AFortAthenaMutator_GiveItemsAtGamePhaseStep : public AFortAthenaMutator diff --git a/Project Reboot 3.0/FortAthenaMutator_Heist.h b/Project Reboot 3.0/FortAthenaMutator_Heist.h new file mode 100644 index 0000000..2b2ef64 --- /dev/null +++ b/Project Reboot 3.0/FortAthenaMutator_Heist.h @@ -0,0 +1,36 @@ +#pragma once + +#include "FortAthenaMutator.h" +#include "GameplayAbilityTypes.h" +#include "reboot.h" + +struct FFortPieSliceSpawnData +{ + FScalableFloat SpawnDirection; // 0x0000(0x0020) (Edit, BlueprintVisible, DisableEditOnInstance, NativeAccessSpecifierPublic) + FScalableFloat SpawnDirectionDeviation; // 0x0020(0x0020) (Edit, BlueprintVisible, DisableEditOnInstance, NativeAccessSpecifierPublic) + FScalableFloat MinSpawnDistanceFromCenter; // 0x0040(0x0020) (Edit, BlueprintVisible, DisableEditOnInstance, NativeAccessSpecifierPublic) + FScalableFloat MaxSpawnDistanceFromCenter; // 0x0060(0x0020) (Edit, BlueprintVisible, DisableEditOnInstance, NativeAccessSpecifierPublic) +}; + +struct FHeistExitCraftSpawnData : public FFortPieSliceSpawnData +{ + FScalableFloat SpawnDelayTime; // 0x0080(0x0020) (Edit, BlueprintVisible, DisableEditOnInstance, NativeAccessSpecifierPublic) + FScalableFloat SafeZonePhaseWhenToSpawn; // 0x00A0(0x0020) (Edit, BlueprintVisible, DisableEditOnInstance, NativeAccessSpecifierPublic) + FScalableFloat SafeZonePhaseWhereToSpawn; // 0x00C0(0x0020) (Edit, BlueprintVisible, DisableEditOnInstance, NativeAccessSpecifierPublic) +}; + +class AFortAthenaMutator_Heist : public AFortAthenaMutator +{ +public: + TArray& GetHeistExitCraftSpawnData() + { + static auto HeistExitCraftSpawnDataOffset = GetOffset("HeistExitCraftSpawnData"); + return Get>(HeistExitCraftSpawnDataOffset); + } + + static UClass* StaticClass() + { + static auto Class = FindObject("/Script/FortniteGame.FortAthenaMutator_Heist"); + return Class; + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/FortAthenaMutator_InventoryOverride.h b/Project Reboot 3.0/FortAthenaMutator_InventoryOverride.h index a305c22..a900b26 100644 --- a/Project Reboot 3.0/FortAthenaMutator_InventoryOverride.h +++ b/Project Reboot 3.0/FortAthenaMutator_InventoryOverride.h @@ -2,7 +2,123 @@ #include "FortAthenaMutator.h" +enum class EAthenaLootDropOverride : uint8_t +{ + NoOverride = 0, + ForceDrop = 1, + ForceKeep = 2, + ForceDestroy = 3, + ForceDropUnlessRespawning = 4, + ForceDestroyUnlessRespawning = 5, + EAthenaLootDropOverride_MAX = 6 +}; + +enum class EAthenaInventorySpawnOverride : uint8_t +{ + NoOverride = 0, + Always = 1, + IntialSpawn = 2, + AircraftPhaseOnly = 3, + EAthenaInventorySpawnOverride_MAX = 4 +}; + +struct FItemLoadoutContainer +{ + TArray Loadout; +}; + +struct FItemLoadoutTeamMap +{ + unsigned char TeamIndex; // 0x0000(0x0001) (Edit, ZeroConstructor, DisableEditOnInstance, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + unsigned char LoadoutIndex; // 0x0001(0x0001) (Edit, ZeroConstructor, DisableEditOnInstance, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + EAthenaInventorySpawnOverride UpdateOverrideType; // 0x0002(0x0001) (Edit, ZeroConstructor, DisableEditOnInstance, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + EAthenaLootDropOverride DropAllItemsOverride; // 0x0003(0x0001) (Edit, BlueprintVisible, ZeroConstructor, DisableEditOnInstance, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) +}; + class AFortAthenaMutator_InventoryOverride : public AFortAthenaMutator { public: + TArray& GetInventoryLoadouts() + { + static auto InventoryLoadoutsOffset = GetOffset("InventoryLoadouts"); + return Get>(InventoryLoadoutsOffset); + } + + TArray& GetTeamLoadouts() + { + static auto TeamLoadoutsOffset = GetOffset("TeamLoadouts"); + return Get>(TeamLoadoutsOffset); + } + + FItemLoadoutTeamMap GetLoadoutTeamForTeamIndex(uint8_t TeamIndex) + { + auto& TeamLoadouts = GetTeamLoadouts(); + + for (int i = 0; i < TeamLoadouts.Num(); i++) + { + auto& TeamLoadout = TeamLoadouts.at(i); + + if (TeamLoadout.TeamIndex == TeamIndex) + return TeamLoadout; + } + + return FItemLoadoutTeamMap(); + } + + FItemLoadoutContainer GetLoadoutContainerForTeamIndex(uint8_t TeamIndex) + { + auto LoadoutTeam = GetLoadoutTeamForTeamIndex(TeamIndex); + + if (LoadoutTeam.TeamIndex == TeamIndex) + { + auto& InventoryLoadouts = GetInventoryLoadouts(); + + if (InventoryLoadouts.Num() - 1 < LoadoutTeam.LoadoutIndex) + return FItemLoadoutContainer(); + + return InventoryLoadouts.at(LoadoutTeam.LoadoutIndex); + } + + return FItemLoadoutContainer(); + } + + EAthenaInventorySpawnOverride& GetInventoryUpdateOverride(uint8_t TeamIndex = 255) + { + if (TeamIndex != 255) + { + auto LoadoutTeam = GetLoadoutTeamForTeamIndex(TeamIndex); + + if (LoadoutTeam.TeamIndex == TeamIndex) + { + if (LoadoutTeam.UpdateOverrideType != EAthenaInventorySpawnOverride::NoOverride) + return LoadoutTeam.UpdateOverrideType; + } + } + + static auto InventoryUpdateOverrideOffset = GetOffset("InventoryUpdateOverride"); + return Get(InventoryUpdateOverrideOffset); + } + + EAthenaLootDropOverride& GetDropAllItemsOverride(uint8_t TeamIndex = 255) + { + if (TeamIndex != 255) + { + auto LoadoutTeam = GetLoadoutTeamForTeamIndex(TeamIndex); + + if (LoadoutTeam.TeamIndex == TeamIndex) + { + if (LoadoutTeam.DropAllItemsOverride != EAthenaLootDropOverride::NoOverride) + return LoadoutTeam.DropAllItemsOverride; + } + } + + static auto DropAllItemsOverrideOffset = GetOffset("DropAllItemsOverride"); + return Get(DropAllItemsOverrideOffset); + } + + static UClass* StaticClass() + { + static auto Class = FindObject("/Script/FortniteGame.FortAthenaMutator_InventoryOverride"); + return Class; + } }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortGameModeAthena.cpp b/Project Reboot 3.0/FortGameModeAthena.cpp index 5591c64..b1abd4c 100644 --- a/Project Reboot 3.0/FortGameModeAthena.cpp +++ b/Project Reboot 3.0/FortGameModeAthena.cpp @@ -24,6 +24,7 @@ #include "OnlineReplStructs.h" #include "BGA.h" #include "vendingmachine.h" +#include "FortAthenaMutator.h" static UFortPlaylist* GetPlaylistToUse() { @@ -212,7 +213,7 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game auto Fortnite_Season = std::floor(Fortnite_Version); - if (false) // Manual foundation showing + // if (false) // Manual foundation showing { if (Fortnite_Season >= 7 && Fortnite_Season <= 10) { @@ -548,6 +549,8 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game Globals::bStartedListening = true; } + bool Ret = false; + if (Engine_Version >= 424) // returning true is stripped on c2+ { static auto WarmupRequiredPlayerCountOffset = GameMode->GetOffset("WarmupRequiredPlayerCount"); @@ -561,13 +564,67 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game // if (MapInfo->Get>(FlightInfosOffset).ArrayNum > 0) { LOG_INFO(LogDev, "ReadyToStartMatch Return Address: 0x{:x}", __int64(_ReturnAddress()) - __int64(GetModuleHandleW(0))); - return true; + Ret = true; } } } } - return Athena_ReadyToStartMatchOriginal(GameMode); + if (!Ret) + Ret = Athena_ReadyToStartMatchOriginal(GameMode); + + if (Ret) + { + // We are assuming it successfully became warmup. + + static auto mutatorClass = FindObject("/Script/FortniteGame.FortAthenaMutator"); + auto AllMutators = UGameplayStatics::GetAllActorsOfClass(GetWorld(), mutatorClass); + + std::vector> FunctionsToCall; + + for (int i = 0; i < AllMutators.Num(); i++) + { + auto Mutator = (AFortAthenaMutator*)AllMutators.at(i); + + FunctionsToCall.push_back(std::make_pair(Mutator, Mutator->FindFunction("OnGamePhaseStepChanged"))); + } + + static int LastNum1 = 3125; + + if (LastNum1 != Globals::AmountOfListens) + { + LastNum1 = Globals::AmountOfListens; + + for (auto& FunctionToCallPair : FunctionsToCall) + { + // On newer versions there is a second param. + + LOG_INFO(LogDev, "FunctionToCallPair.second: {}", __int64(FunctionToCallPair.second)); + + if (FunctionToCallPair.second) + { + if (Fortnite_Version < 10) + { + // mem leak btw + + auto a = ConstructOnGamePhaseStepChangedParams(EAthenaGamePhaseStep::None); + + if (a) + { + FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, a); + FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, ConstructOnGamePhaseStepChangedParams(EAthenaGamePhaseStep::Setup)); + FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, ConstructOnGamePhaseStepChangedParams(EAthenaGamePhaseStep::Warmup)); + // FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, &StormFormingGamePhaseStep); + // FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, &StormHoldingGamePhaseStep); + // FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, &StormShrinkingGamePhaseStep); + } + } + } + } + } + } + + return Ret; } int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint8 preferredTeam, AActor* Controller) @@ -580,11 +637,26 @@ int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint static auto CurrentPlaylistDataOffset = GameState->GetOffset("CurrentPlaylistData", false); - UObject* Playlist = nullptr; + UFortPlaylist* Playlist = nullptr; + + bool bVersionHasPlaylist = false; + + if (CurrentPlaylistDataOffset != -1 || Fortnite_Version >= 6) + { + bVersionHasPlaylist = true; + Playlist = CurrentPlaylistDataOffset == -1 && Fortnite_Version < 6 ? nullptr : GameState->GetCurrentPlaylist(); + } static int DefaultFirstTeam = 3; + + if (Playlist) + { + static auto bIsLargeTeamGameOffset = Playlist->GetOffset("bIsLargeTeamGame"); + bool bIsLargeTeamGame = Playlist->Get(bIsLargeTeamGameOffset); + } + static int CurrentTeamMembers = 0; // bad - static int Current = 3; + static int Current = DefaultFirstTeam; static int LastNum = 1; @@ -592,7 +664,7 @@ int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint { LastNum = AmountOfRestarts; - Current = 3; + Current = DefaultFirstTeam; CurrentTeamMembers = 0; } @@ -603,10 +675,8 @@ int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint bool bShouldSpreadTeams = false; - if (CurrentPlaylistDataOffset != -1 || Fortnite_Version >= 6) + if (bVersionHasPlaylist) { - Playlist = CurrentPlaylistDataOffset == -1 && Fortnite_Version < 6 ? nullptr : GameState->GetCurrentPlaylist(); - if (!Playlist) { CurrentTeamMembers = 0; @@ -754,7 +824,7 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena // CurrentActor->K2_DestroyActor(); // continue; - if (Engine_Version != 419) + // if (Engine_Version != 419) { auto Location = CurrentActor->GetActorLocation(); Location.Z += UpZ; diff --git a/Project Reboot 3.0/FortGameStateAthena.cpp b/Project Reboot 3.0/FortGameStateAthena.cpp index 650443e..85fad13 100644 --- a/Project Reboot 3.0/FortGameStateAthena.cpp +++ b/Project Reboot 3.0/FortGameStateAthena.cpp @@ -9,6 +9,33 @@ } */ +TScriptInterface AFortGameStateAthena::GetSafeZoneInterface() +{ + int Offset = -1; + + if (Fortnite_Version == 10.40) + { + // Offset = 0xF60; + } + + TScriptInterface ScriptInterface{}; + + if (Offset != -1) + { + auto idk = (void*)(__int64(this) + Offset); + + UObject* ObjectPtr = reinterpret_cast(((UObject*)idk)->VFTable[0x1])(__int64(idk)); // not actually a uobject but its just how we can get vft + + if (ObjectPtr) + { + ScriptInterface.ObjectPointer = ObjectPtr; + ScriptInterface.InterfacePointer = ObjectPtr->GetInterfaceAddress(UFortSafeZoneInterface::StaticClass()); + } + } + + return ScriptInterface; +} + UFortPlaylist*& AFortGameStateAthena::GetCurrentPlaylist() { static auto CurrentPlaylistInfoOffset = GetOffset("CurrentPlaylistInfo", false); diff --git a/Project Reboot 3.0/FortGameStateAthena.h b/Project Reboot 3.0/FortGameStateAthena.h index 3ebc8a0..671c735 100644 --- a/Project Reboot 3.0/FortGameStateAthena.h +++ b/Project Reboot 3.0/FortGameStateAthena.h @@ -4,6 +4,26 @@ #include "FortPlayerState.h" #include "FortPlaylist.h" #include "BuildingStructuralSupportSystem.h" +#include "ScriptInterface.h" +#include "Interface.h" + +enum class EAthenaGamePhaseStep : uint8_t // idk if this changes +{ + None = 0, + Setup = 1, + Warmup = 2, + GetReady = 3, + BusLocked = 4, + BusFlying = 5, + StormForming = 6, + StormHolding = 7, + StormShrinking = 8, + Countdown = 9, + FinalCountdown = 10, + EndGame = 11, + Count = 12, + EAthenaGamePhaseStep_MAX = 13 +}; enum class EAthenaGamePhase : uint8_t { @@ -17,6 +37,16 @@ enum class EAthenaGamePhase : uint8_t EAthenaGamePhase_MAX = 7 }; +class UFortSafeZoneInterface : public UInterface +{ +public: + static UClass* StaticClass() + { + static auto Struct = FindObject("/Script/FortniteGame.FortSafeZoneInterface"); + return Struct; + } +}; + struct FPlayerBuildableClassContainer { TArray BuildingClasses; // 0x0000(0x0010) (ZeroConstructor, Transient, UObjectWrapper, NativeAccessSpecifierPublic) @@ -50,6 +80,7 @@ public: } UFortPlaylist*& GetCurrentPlaylist(); + TScriptInterface GetSafeZoneInterface(); // void AddPlayerStateToGameMemberInfo(class AFortPlayerStateAthena* PlayerState); @@ -58,4 +89,36 @@ public: bool IsPlayerBuildableClass(UClass* Class); void OnRep_GamePhase(); void OnRep_CurrentPlaylistInfo(); -}; \ No newline at end of file +}; + +static void* ConstructOnGamePhaseStepChangedParams(EAthenaGamePhaseStep GamePhaseStep) +{ + struct AFortAthenaAIBotController_OnGamePhaseStepChanged_Params + { + TScriptInterface SafeZoneInterface; // (ConstParm, Parm, OutParm, ZeroConstructor, ReferenceParm, IsPlainOldData, NoDestructor, UObjectWrapper, NativeAccessSpecifierPublic) + EAthenaGamePhaseStep GamePhaseStep; // (ConstParm, Parm, ZeroConstructor, IsPlainOldData, NoDestructor, HasGetValueTypeHash, NativeAccessSpecifierPublic) + }; + + bool bHasSafeZoneInterfaceParam = Fortnite_Version >= 10; // idk what version + + AFortAthenaAIBotController_OnGamePhaseStepChanged_Params* Params = Alloc(); + + if (bHasSafeZoneInterfaceParam) + { + auto GameState = (AFortGameStateAthena*)GetWorld()->GetGameState(); + + auto Interface = GameState->GetSafeZoneInterface(); + + if (!Interface.ObjectPointer) + return nullptr; + + Params->SafeZoneInterface = Interface; + Params->GamePhaseStep = GamePhaseStep; + } + else + { + *(EAthenaGamePhaseStep*)(__int64(Params) + 0) = GamePhaseStep; + } + + return Params; +} \ No newline at end of file diff --git a/Project Reboot 3.0/FortGameplayAbilityAthena_PeriodicItemGrant.h b/Project Reboot 3.0/FortGameplayAbilityAthena_PeriodicItemGrant.h index 2d35103..3282c9b 100644 --- a/Project Reboot 3.0/FortGameplayAbilityAthena_PeriodicItemGrant.h +++ b/Project Reboot 3.0/FortGameplayAbilityAthena_PeriodicItemGrant.h @@ -3,10 +3,54 @@ #include "Object.h" #include "Stack.h" +#include "GameplayAbilityTypes.h" +#include "FortWorldItemDefinition.h" + +struct FActiveItemGrantInfo +{ + static UStruct* GetStruct() + { + static auto Struct = FindObject("/Script/FortniteGame.ActiveItemGrantInfo"); + return Struct; + } + + static int GetStructSize() { return GetStruct()->GetPropertiesSize(); } + + UFortWorldItemDefinition*& GetItem() + { + static auto ItemOffset = FindOffsetStruct("/Script/FortniteGame.ActiveItemGrantInfo", "Item"); + return *(UFortWorldItemDefinition**)(__int64(this) + ItemOffset); + } + + FScalableFloat& GetAmountToGive() + { + static auto AmountToGiveOffset = FindOffsetStruct("/Script/FortniteGame.ActiveItemGrantInfo", "AmountToGive"); + return *(FScalableFloat*)(__int64(this) + AmountToGiveOffset); + } + + FScalableFloat& GetMaxAmount() + { + static auto MaxAmountOffset = FindOffsetStruct("/Script/FortniteGame.ActiveItemGrantInfo", "MaxAmount"); + return *(FScalableFloat*)(__int64(this) + MaxAmountOffset); + } +}; + class UFortGameplayAbilityAthena_PeriodicItemGrant : public UObject // UFortGameplayAbility { public: static inline void (*StartItemAwardTimersOriginal)(UObject* Context, FFrame& Stack, void* Ret); + TMap& GetItemsToGrant() + { + static auto ItemsToGrantOffset = GetOffset("ItemsToGrant"); + return Get>(ItemsToGrantOffset); + } + + TArray& GetActiveTimers() + { + static auto ActiveTimersOffset = GetOffset("ActiveTimers"); + return Get>(ActiveTimersOffset); + } + static void StartItemAwardTimersHook(UObject* Context, FFrame& Stack, void* Ret); }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortInventory.cpp b/Project Reboot 3.0/FortInventory.cpp index f73026f..e560853 100644 --- a/Project Reboot 3.0/FortInventory.cpp +++ b/Project Reboot 3.0/FortInventory.cpp @@ -160,13 +160,8 @@ std::pair, std::vector> AFortInventory::AddI auto ReplicatedEntryIdx = GetItemList().GetReplicatedEntries().Add(*NewItemInstance->GetItemEntry(), FortItemEntrySize); // GetItemList().GetReplicatedEntries().AtPtr(ReplicatedEntryIdx, FFortItemEntry::GetStructSize())->GetIsReplicatedCopy() = true; - if (WorldItemDefinition->IsValidLowLevel()) + if (FortPlayerController && WorldItemDefinition->IsValidLowLevel()) { - if (WorldItemDefinition->ShouldFocusWhenAdded()) // Should we also do this for stacking? - { - FortPlayerController->ServerExecuteInventoryItemHook(FortPlayerController, NewItemInstance->GetItemEntry()->GetItemGuid()); - } - bool AreGadgetsEnabled = Addresses::ApplyGadgetData && Addresses::RemoveGadgetData && Globals::bEnableAGIDs; if (AreGadgetsEnabled) @@ -180,10 +175,25 @@ std::pair, std::vector> AFortInventory::AddI bool (*ApplyGadgetData)(UFortGadgetItemDefinition * a1, __int64 a2, UFortItem* a3, unsigned __int8 a4) = decltype(ApplyGadgetData)(Addresses::ApplyGadgetData); static auto FortInventoryOwnerInterfaceClass = FindObject("/Script/FortniteGame.FortInventoryOwnerInterface"); - auto Interface = __int64(PlayerController->GetInterfaceAddress(FortInventoryOwnerInterfaceClass)); + auto Interface = __int64(FortPlayerController->GetInterfaceAddress(FortInventoryOwnerInterfaceClass)); LOG_INFO(LogDev, "Res: {}", ApplyGadgetData(GadgetItemDefinition, Interface, NewItemInstance, true)); + + if (Fortnite_Version < 7) + { + auto PickaxeInstance = GetPickaxeInstance(); + + if (PickaxeInstance) + { + RemoveItem(PickaxeInstance->GetItemEntry()->GetItemGuid(), nullptr, PickaxeInstance->GetItemEntry()->GetCount(), true); + } + } } } + + if (WorldItemDefinition->ShouldFocusWhenAdded()) // Should we also do this for stacking? + { + FortPlayerController->ServerExecuteInventoryItemHook(FortPlayerController, NewItemInstance->GetItemEntry()->GetItemGuid()); + } } else { @@ -343,6 +353,21 @@ bool AFortInventory::RemoveItem(const FGuid& ItemGuid, bool* bShouldUpdate, int { LOG_INFO(LogDev, "Unequipping Gadget!"); GadgetItemDefinition->UnequipGadgetData(FortPlayerController, ItemInstances.at(i)); + + if (Fortnite_Version < 7) + { + auto CosmeticLoadout = FortPlayerController->GetCosmeticLoadout(); + // LOG_INFO(LogDev, "CosmeticLoadout: {}", __int64(CosmeticLoadout)); + auto CosmeticLoadoutPickaxe = CosmeticLoadout ? CosmeticLoadout->GetPickaxe() : nullptr; + // LOG_INFO(LogDev, "CosmeticLoadoutPickaxe: {}", __int64(CosmeticLoadoutPickaxe)); + // LOG_INFO(LogDev, "CosmeticLoadoutPickaxe Name: {}", CosmeticLoadoutPickaxe ? CosmeticLoadoutPickaxe->GetFullName() : "InvalidObject"); + static auto WeaponDefinitionOffset = FindOffsetStruct("/Script/FortniteGame.AthenaPickaxeItemDefinition", "WeaponDefinition"); + + auto PickaxeDefinition = CosmeticLoadoutPickaxe ? CosmeticLoadoutPickaxe->Get(WeaponDefinitionOffset) + : FindObject(L"/Game/Athena/Items/Weapons/WID_Harvest_Pickaxe_Athena_C_T01.WID_Harvest_Pickaxe_Athena_C_T01"); + + this->AddItem(PickaxeDefinition, nullptr); + } } } diff --git a/Project Reboot 3.0/FortPlayerController.cpp b/Project Reboot 3.0/FortPlayerController.cpp index 66c189d..2eebb5d 100644 --- a/Project Reboot 3.0/FortPlayerController.cpp +++ b/Project Reboot 3.0/FortPlayerController.cpp @@ -890,7 +890,7 @@ void AFortPlayerController::ServerAttemptInventoryDropHook(AFortPlayerController if (!ItemDefinition || !ItemDefinition->CanBeDropped()) return; - if (!ItemDefinition->ShouldIgnoreRespawningOnDrop()) + if (!ItemDefinition->ShouldIgnoreRespawningOnDrop() && ItemDefinition->GetDropBehavior() != EWorldItemDropBehavior::DestroyOnDrop) { auto Pickup = AFortPickup::SpawnPickup(ReplicatedEntry, Pawn->GetActorLocation(), EFortPickupSourceTypeFlag::Player, EFortPickupSpawnSource::Unset, Pawn); diff --git a/Project Reboot 3.0/FortPlayerControllerAthena.cpp b/Project Reboot 3.0/FortPlayerControllerAthena.cpp index 00c3c36..1ab4119 100644 --- a/Project Reboot 3.0/FortPlayerControllerAthena.cpp +++ b/Project Reboot 3.0/FortPlayerControllerAthena.cpp @@ -9,6 +9,72 @@ #include "FortAthenaMutator_GiveItemsAtGamePhaseStep.h" #include "DataTableFunctionLibrary.h" +void AFortPlayerControllerAthena::StartGhostModeHook(UObject* Context, FFrame* Stack, void* Ret) +{ + LOG_INFO(LogDev, __FUNCTION__); + + auto Controller = (AFortPlayerControllerAthena*)Context; + + UFortWorldItemDefinition* ItemProvidingGhostMode = nullptr; + + Stack->StepCompiledIn(&ItemProvidingGhostMode); + + if (!ItemProvidingGhostMode) + return StartGhostModeOriginal(Context, Stack, Ret); + + // if (!Controller->HasAuthority) return StartGhostModeOriginal(Context, Stack, Ret); + + // if (Controller->GhostModeRepData.bInGhostMode) return StartGhostModeOriginal(Context, Stack, Ret); + + auto WorldInventory = Controller->GetWorldInventory(); + + if (!WorldInventory) + return StartGhostModeOriginal(Context, Stack, Ret); + + bool bShouldUpdate = false; + auto NewAndModifiedInstances = WorldInventory->AddItem(ItemProvidingGhostMode, &bShouldUpdate, 1); + auto GhostModeItemInstance = NewAndModifiedInstances.first[0]; + + if (!GhostModeItemInstance) + return StartGhostModeOriginal(Context, Stack, Ret); + + if (bShouldUpdate) + WorldInventory->Update(); + + Controller->ServerExecuteInventoryItemHook(Controller, GhostModeItemInstance->GetItemEntry()->GetItemGuid()); + + return StartGhostModeOriginal(Context, Stack, Ret); +} + +void AFortPlayerControllerAthena::EndGhostModeHook(AFortPlayerControllerAthena* PlayerController) +{ + // I believe there are a lot of other places we should remove it (go to XREFs of K2_RemoveItemFromPlayer on a version like 6.21, and there will be something checking ghost stuff). + + LOG_INFO(LogDev, __FUNCTION__); + + auto WorldInventory = PlayerController->GetWorldInventory(); + + if (!WorldInventory) + return EndGhostModeOriginal(PlayerController); + + auto GhostModeRepData = PlayerController->GetGhostModeRepData(); + + auto GhostModeItemDef = GhostModeRepData->GetGhostModeItemDef(); + auto GhostModeItemInstance = WorldInventory->FindItemInstance(GhostModeItemDef); + + if (GhostModeItemInstance) + { + bool bShouldUpdate = false; + int Count = 1; // GhostModeItemInstance->GetItemEntry()->GetCount() + WorldInventory->RemoveItem(GhostModeItemInstance->GetItemEntry()->GetItemGuid(), &bShouldUpdate, Count); + + if (bShouldUpdate) + WorldInventory->Update(); + } + + return EndGhostModeOriginal(PlayerController); +} + void AFortPlayerControllerAthena::EnterAircraftHook(UObject* PC, AActor* Aircraft) { auto PlayerController = Cast(Engine_Version < 424 ? PC : ((UActorComponent*)PC)->GetOwner()); @@ -16,7 +82,7 @@ void AFortPlayerControllerAthena::EnterAircraftHook(UObject* PC, AActor* Aircraf if (!PlayerController) return; - LOG_INFO(LogDev, "EnterAircraftHook"); + // LOG_INFO(LogDev, "EnterAircraftHook"); EnterAircraftOriginal(PC, Aircraft); @@ -55,36 +121,49 @@ void AFortPlayerControllerAthena::EnterAircraftHook(UObject* PC, AActor* Aircraf static auto mutatorClass = FindObject("/Script/FortniteGame.FortAthenaMutator"); auto AllMutators = UGameplayStatics::GetAllActorsOfClass(GetWorld(), mutatorClass); + std::vector> FunctionsToCall; + for (int i = 0; i < AllMutators.Num(); i++) { - auto Mutator = AllMutators.at(i); + auto Mutator = (AFortAthenaMutator*)AllMutators.at(i); LOG_INFO(LogDev, "[{}] Mutator: {}", i, Mutator->GetFullName()); + FunctionsToCall.push_back(std::make_pair(Mutator, Mutator->FindFunction("OnGamePhaseStepChanged"))); + if (auto GiveItemsAtGamePhaseStepMutator = Cast(Mutator)) { auto PhaseToGive = GiveItemsAtGamePhaseStepMutator->GetPhaseToGiveItems(); - - LOG_INFO(LogDev, "[{}] PhaseToGiveItems: {}", i, (int)PhaseToGive); - auto& ItemsToGive = GiveItemsAtGamePhaseStepMutator->GetItemsToGive(); - LOG_INFO(LogDev, "[{}] ItemsToGive.Num(): {}", i, ItemsToGive.Num()); + LOG_INFO(LogDev, "[{}] PhaseToGiveItems: {} ItemsToGive.Num(): {}", i, (int)PhaseToGive, ItemsToGive.Num()); if (PhaseToGive <= 5) // Flying or lower { for (int j = 0; j < ItemsToGive.Num(); j++) { - auto& ItemToGive = ItemsToGive.at(j); + auto ItemToGive = ItemsToGive.AtPtr(j, FItemsToGive::GetStructSize()); + + if (!ItemToGive->GetItemToDrop()) + continue; float Out = 1; FString ContextString; EEvaluateCurveTableResult result; - // UDataTableFunctionLibrary::EvaluateCurveTableRow(ItemToGive.NumberToGive.GetCurve().CurveTable, ItemToGive.NumberToGive.GetCurve().RowName, 0.f, ContextString, &result, &Out); + float Out2 = 0; - LOG_INFO(LogDev, "Out: {}", Out); + if (!IsBadReadPtr(ItemToGive->GetNumberToGive().GetCurve().CurveTable, 8) && ItemToGive->GetNumberToGive().GetCurve().RowName.IsValid()) + { + UDataTableFunctionLibrary::EvaluateCurveTableRow(ItemToGive->GetNumberToGive().GetCurve().CurveTable, ItemToGive->GetNumberToGive().GetCurve().RowName, + 0.f, ContextString, &result, &Out2); + } - WorldInventory->AddItem(ItemToGive.ItemToDrop, nullptr, Out); + LOG_INFO(LogDev, "[{}] [{}] Out: {} Out2: {} ItemToGive.ItemToDrop: {}", i, j, Out, Out2, ItemToGive->GetItemToDrop()->IsValidLowLevel() ? ItemToGive->GetItemToDrop()->GetFullName() : "BadRead"); + + if (!Out2) + continue; + + WorldInventory->AddItem(ItemToGive->GetItemToDrop(), nullptr, Out2); } } } @@ -101,7 +180,43 @@ void AFortPlayerControllerAthena::EnterAircraftHook(UObject* PC, AActor* Aircraf } */ } + static int LastNum1 = 3125; + + if (LastNum1 != Globals::AmountOfListens) + { + LastNum1 = Globals::AmountOfListens; + + for (auto& FunctionToCallPair : FunctionsToCall) + { + // On newer versions there is a second param. + + LOG_INFO(LogDev, "FunctionToCallPair.second: {}", __int64(FunctionToCallPair.second)); + + if (FunctionToCallPair.second) + { + { + // mem leak btw + + auto a = ConstructOnGamePhaseStepChangedParams(EAthenaGamePhaseStep::GetReady); + + if (a) + { + FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, a); + FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, ConstructOnGamePhaseStepChangedParams(EAthenaGamePhaseStep::BusLocked)); + FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, ConstructOnGamePhaseStepChangedParams(EAthenaGamePhaseStep::BusFlying)); + } + + // FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, &StormFormingGamePhaseStep); + // FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, &StormHoldingGamePhaseStep); + // FunctionToCallPair.first->ProcessEvent(FunctionToCallPair.second, &StormShrinkingGamePhaseStep); + } + } + } + } + WorldInventory->Update(); + + // Should we equip the pickaxe for older builds here? } void AFortPlayerControllerAthena::ServerRequestSeatChangeHook(AFortPlayerControllerAthena* PlayerController, int TargetSeatIndex) diff --git a/Project Reboot 3.0/FortPlayerControllerAthena.h b/Project Reboot 3.0/FortPlayerControllerAthena.h index eb87bc5..1d1282a 100644 --- a/Project Reboot 3.0/FortPlayerControllerAthena.h +++ b/Project Reboot 3.0/FortPlayerControllerAthena.h @@ -73,6 +73,21 @@ static void ApplyCID(AFortPlayerPawn* Pawn, UObject* CID, bool bUseServerChooseP // PlayerState->Get(HeroTypeOffset) = HeroDefinition; } +struct FGhostModeRepData +{ + bool& IsInGhostMode() + { + static auto bInGhostModeOffset = FindOffsetStruct("/Script/FortniteGame.GhostModeRepData", "bInGhostMode"); + return *(bool*)(__int64(this) + bInGhostModeOffset); + } + + UFortWorldItemDefinition*& GetGhostModeItemDef() + { + static auto GhostModeItemDefOffset = FindOffsetStruct("/Script/FortniteGame.GhostModeRepData", "GhostModeItemDef"); + return *(UFortWorldItemDefinition**)(__int64(this) + GhostModeItemDefOffset); + } +}; + class AFortPlayerControllerAthena : public AFortPlayerController { public: @@ -80,18 +95,28 @@ public: static inline void (*ServerReadyToStartMatchOriginal)(AFortPlayerControllerAthena* PlayerController); static inline void (*ServerRequestSeatChangeOriginal)(AFortPlayerControllerAthena* PlayerController, int TargetSeatIndex); static inline void (*EnterAircraftOriginal)(UObject* PC, AActor* Aircraft); + static inline void (*StartGhostModeOriginal)(UObject* Context, FFrame* Stack, void* Ret); + static inline void (*EndGhostModeOriginal)(AFortPlayerControllerAthena* PlayerController); AFortPlayerStateAthena* GetPlayerStateAthena() { return (AFortPlayerStateAthena*)GetPlayerState(); } + FGhostModeRepData* GetGhostModeRepData() + { + static auto GhostModeRepDataOffset = GetOffset("GhostModeRepData"); + return GetPtr(GhostModeRepDataOffset); + } + UAthenaMarkerComponent* GetMarkerComponent() { static auto MarkerComponentOffset = GetOffset("MarkerComponent"); return Get(MarkerComponentOffset); } + static void StartGhostModeHook(UObject* Context, FFrame* Stack, void* Ret); // we could native hook this but eh + static void EndGhostModeHook(AFortPlayerControllerAthena* PlayerController); static void EnterAircraftHook(UObject* PC, AActor* Aircraft); static void ServerRequestSeatChangeHook(AFortPlayerControllerAthena* PlayerController, int TargetSeatIndex); // actually in zone static void ServerRestartPlayerHook(AFortPlayerControllerAthena* Controller); diff --git a/Project Reboot 3.0/FortPlayerPawn.cpp b/Project Reboot 3.0/FortPlayerPawn.cpp index 597af0f..d9cdc65 100644 --- a/Project Reboot 3.0/FortPlayerPawn.cpp +++ b/Project Reboot 3.0/FortPlayerPawn.cpp @@ -1,6 +1,7 @@ #include "FortPlayerPawn.h" #include #include "FortPlayerController.h" +#include "FortGadgetItemDefinition.h" void AFortPlayerPawn::ServerChoosePart(EFortCustomPartType Part, UObject* ChosenCharacterPart) { @@ -122,6 +123,36 @@ void AFortPlayerPawn::UnEquipVehicleWeaponDefinition(UFortWeaponItemDefinition* AFortPlayerController::ServerExecuteInventoryItemHook(PlayerController, PickaxeInstance->GetItemEntry()->GetItemGuid()); // Bad, we should equip the last weapon. } +void AFortPlayerPawn::StartGhostModeExitHook(UObject* Context, FFrame* Stack, void* Ret) +{ + LOG_INFO(LogDev, __FUNCTION__); + + auto Pawn = (AFortPlayerPawn*)Context; + + auto Controller = Cast(Pawn->GetController()); + + if (!Controller) + return; + + auto WorldInventory = Controller->GetWorldInventory(); + + auto SpookyMistItemDefinition = FindObject("/Game/Athena/Items/Gameplay/SpookyMist/AGID_SpookyMist.AGID_SpookyMist"); + auto SpookyMistInstance = WorldInventory->FindItemInstance(SpookyMistItemDefinition); + + if (SpookyMistInstance) + { + bool bShouldUpdate = false; + WorldInventory->RemoveItem(SpookyMistInstance->GetItemEntry()->GetItemGuid(), &bShouldUpdate, 1, true); + + if (bShouldUpdate) + WorldInventory->Update(); + + Controller->ApplyCosmeticLoadout(); + } + + return StartGhostModeExitOriginal(Context, Stack, Ret); +} + AActor* AFortPlayerPawn::ServerOnExitVehicleHook(AFortPlayerPawn* PlayerPawn, ETryExitVehicleBehavior ExitForceBehavior) { auto VehicleWeaponDefinition = PlayerPawn->GetVehicleWeaponDefinition(PlayerPawn->GetVehicle()); diff --git a/Project Reboot 3.0/FortPlayerPawn.h b/Project Reboot 3.0/FortPlayerPawn.h index 556316b..8b5dc43 100644 --- a/Project Reboot 3.0/FortPlayerPawn.h +++ b/Project Reboot 3.0/FortPlayerPawn.h @@ -32,6 +32,7 @@ class AFortPlayerPawn : public AFortPawn { public: static inline AActor* (*ServerOnExitVehicleOriginal)(AFortPlayerPawn* Pawn, ETryExitVehicleBehavior ExitForceBehavior); // actually returns AFortAthenaVehicle + static inline void (*StartGhostModeExitOriginal)(UObject* Context, FFrame* Stack, void* Ret); void ServerChoosePart(EFortCustomPartType Part, class UObject* ChosenCharacterPart); void ForceLaunchPlayerZipline(); // Thanks android @@ -41,6 +42,7 @@ public: UFortWeaponItemDefinition* GetVehicleWeaponDefinition(AFortAthenaVehicle* Vehicle); void UnEquipVehicleWeaponDefinition(UFortWeaponItemDefinition* VehicleWeaponDefinition); + static void StartGhostModeExitHook(UObject* Context, FFrame* Stack, void* Ret); // we could native hook this but eh static AActor* ServerOnExitVehicleHook(AFortPlayerPawn* Pawn, ETryExitVehicleBehavior ExitForceBehavior); // actually returns AFortAthenaVehicle 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/FortWorldItemDefinition.h b/Project Reboot 3.0/FortWorldItemDefinition.h index dda9a15..7d3c744 100644 --- a/Project Reboot 3.0/FortWorldItemDefinition.h +++ b/Project Reboot 3.0/FortWorldItemDefinition.h @@ -2,6 +2,14 @@ #include "FortItemDefinition.h" +enum class EWorldItemDropBehavior : uint8_t +{ + DropAsPickup = 0, + DestroyOnDrop = 1, + DropAsPickupDestroyOnEmpty = 2, + EWorldItemDropBehavior_MAX = 3 +}; + class UFortWorldItemDefinition : public UFortItemDefinition { public: @@ -12,6 +20,12 @@ public: return ReadBitfieldValue(bCanBeDroppedOffset, bCanBeDroppedFieldMask); } + EWorldItemDropBehavior& GetDropBehavior() + { + static auto DropBehaviorOffset = GetOffset("DropBehavior"); + return Get(DropBehaviorOffset); + } + bool ShouldDropOnDeath() { static auto bDropOnDeathOffset = GetOffset("bDropOnDeath"); diff --git a/Project Reboot 3.0/Function.h b/Project Reboot 3.0/Function.h new file mode 100644 index 0000000..6d3de55 --- /dev/null +++ b/Project Reboot 3.0/Function.h @@ -0,0 +1,56 @@ +// FROM 4.23 + +#pragma once + +#include "TypeCompatibleBytes.h" + +#if defined(_WIN32) && !defined(_WIN64) +// Don't use inline storage on Win32, because that will affect the alignment of TFunction, and we can't pass extra-aligned types by value on Win32. +#define TFUNCTION_USES_INLINE_STORAGE 0 +#elif USE_SMALL_TFUNCTIONS +#define TFUNCTION_USES_INLINE_STORAGE 0 +#else +#define TFUNCTION_USES_INLINE_STORAGE 1 +#define TFUNCTION_INLINE_SIZE 32 +#define TFUNCTION_INLINE_ALIGNMENT 16 +#endif + +template +struct TFunctionRefBase; + +template +struct TFunctionRefBase +{ + Ret(*Callable)(void*, ParamTypes&...); + + StorageType Storage; + +#if ENABLE_TFUNCTIONREF_VISUALIZATION + // To help debug visualizers + TDebugHelper DebugPtrStorage; +#endif +}; + +struct FFunctionStorage +{ + FFunctionStorage() + : HeapAllocation(nullptr) + { + } + + void* HeapAllocation; +#if TFUNCTION_USES_INLINE_STORAGE + // Inline storage for an owned object + TAlignedBytes InlineAllocation; +#endif +}; + +template +struct TFunctionStorage : FFunctionStorage +{ +}; + +template +class TFunction final : public TFunctionRefBase, FuncType> +{ +}; \ No newline at end of file diff --git a/Project Reboot 3.0/GameInstance.h b/Project Reboot 3.0/GameInstance.h new file mode 100644 index 0000000..13d172a --- /dev/null +++ b/Project Reboot 3.0/GameInstance.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Object.h" + +#include "TimerManager.h" + +class UGameInstance : + public UObject +{ +public: + inline FTimerManager& GetTimerManager() const + { + static auto TimerManagerOffset = 0x90; + return **(FTimerManager**)(__int64(TimerManagerOffset)); + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/GameModeBase.cpp b/Project Reboot 3.0/GameModeBase.cpp index a44e227..8b1b06d 100644 --- a/Project Reboot 3.0/GameModeBase.cpp +++ b/Project Reboot 3.0/GameModeBase.cpp @@ -7,6 +7,7 @@ #include "FortAthenaMutator_GiveItemsAtGamePhaseStep.h" #include "DataTableFunctionLibrary.h" #include "FortAthenaMutator_GG.h" +#include "FortAthenaMutator_InventoryOverride.h" UClass* AGameModeBase::GetDefaultPawnClassForController(AController* InController) { @@ -44,16 +45,16 @@ APawn* AGameModeBase::SpawnDefaultPawnForHook(AGameModeBase* GameMode, AControll if constexpr (bUseSpawnActor) { -NewPawn = GetWorld()->SpawnActor(PawnClass, SpawnTransform, SpawnParameters); + NewPawn = GetWorld()->SpawnActor(PawnClass, SpawnTransform, SpawnParameters); } else { - struct { AController* NewPlayer; FTransform SpawnTransform; APawn* ReturnValue; } - AGameModeBase_SpawnDefaultPawnAtTransform_Params{ NewPlayer, SpawnTransform }; + struct { AController* NewPlayer; FTransform SpawnTransform; APawn* ReturnValue; } + AGameModeBase_SpawnDefaultPawnAtTransform_Params{ NewPlayer, SpawnTransform }; - GameMode->ProcessEvent(fn, &AGameModeBase_SpawnDefaultPawnAtTransform_Params); + GameMode->ProcessEvent(fn, &AGameModeBase_SpawnDefaultPawnAtTransform_Params); - NewPawn = AGameModeBase_SpawnDefaultPawnAtTransform_Params.ReturnValue; + NewPawn = AGameModeBase_SpawnDefaultPawnAtTransform_Params.ReturnValue; } if (!NewPawn) @@ -66,12 +67,16 @@ NewPawn = GetWorld()->SpawnActor(PawnClass, SpawnTransform, SpawnParamete auto NewPlayerAsAthena = Cast(NewPlayer); auto GameState = ((AFortGameModeAthena*)GameMode)->GetGameStateAthena(); + auto PlayerStateAthena = NewPlayerAsAthena->GetPlayerStateAthena(); + + if (!PlayerStateAthena) + return nullptr; GET_PLAYLIST(GameState); if (CurrentPlaylist) { - CurrentPlaylist->ApplyModifiersToActor(NewPlayerAsAthena->GetPlayerState()); // We need to move this! + CurrentPlaylist->ApplyModifiersToActor(PlayerStateAthena); // We need to move this! } /* if (Fortnite_Version >= 18) @@ -90,6 +95,8 @@ NewPawn = GetWorld()->SpawnActor(PawnClass, SpawnTransform, SpawnParamete if (!WorldInventory->GetPickaxeInstance()) { + // TODO Check Playlist->bRequirePickaxeInStartingInventory + auto CosmeticLoadout = NewPlayerAsAthena->GetCosmeticLoadout(); // LOG_INFO(LogDev, "CosmeticLoadout: {}", __int64(CosmeticLoadout)); auto CosmeticLoadoutPickaxe = CosmeticLoadout ? CosmeticLoadout->GetPickaxe() : nullptr; @@ -134,6 +141,23 @@ NewPawn = GetWorld()->SpawnActor(PawnClass, SpawnTransform, SpawnParamete } } */ + auto AddInventoryOverrideTeamLoadouts = [&](AFortAthenaMutator* Mutator) + { + if (auto InventoryOverride = Cast(Mutator)) + { + auto TeamIndex = PlayerStateAthena->GetTeamIndex(); + auto LoadoutContainer = InventoryOverride->GetLoadoutContainerForTeamIndex(TeamIndex); + + for (int i = 0; i < LoadoutContainer.Loadout.Num(); i++) + { + auto& ItemAndCount = LoadoutContainer.Loadout.at(i); + WorldInventory->AddItem(ItemAndCount.GetItem(), nullptr, ItemAndCount.GetCount()); + } + } + }; + + LoopMutators(AddInventoryOverrideTeamLoadouts); + WorldInventory->Update(); } } diff --git a/Project Reboot 3.0/GenericPlatformMisc.h b/Project Reboot 3.0/GenericPlatformMisc.h new file mode 100644 index 0000000..3f59c93 --- /dev/null +++ b/Project Reboot 3.0/GenericPlatformMisc.h @@ -0,0 +1,2 @@ +#pragma once + diff --git a/Project Reboot 3.0/Interface.h b/Project Reboot 3.0/Interface.h new file mode 100644 index 0000000..025d387 --- /dev/null +++ b/Project Reboot 3.0/Interface.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Object.h" + +/** + * Base class for all interfaces + * + */ + +class UInterface : public UObject +{ + // DECLARE_CLASS_INTRINSIC(UInterface, UObject, CLASS_Interface | CLASS_Abstract, TEXT("/Script/CoreUObject")) +}; + +class IInterface +{ +protected: + + virtual ~IInterface() {} + +public: + + typedef UInterface UClassType; +}; \ No newline at end of file diff --git a/Project Reboot 3.0/NetDriver.cpp b/Project Reboot 3.0/NetDriver.cpp index 24dd4bd..b2e027b 100644 --- a/Project Reboot 3.0/NetDriver.cpp +++ b/Project Reboot 3.0/NetDriver.cpp @@ -7,13 +7,18 @@ #include "GameplayStatics.h" #include "KismetMathLibrary.h" #include -#include "GenericPlatformMath.h" -#include "ActorChannel.h" -#include "KismetSystemLibrary.h" -#include "UnrealMathUtility.h" -#include "FortQuickBars.h" -#include "Sort.h" -#include "Sorting.h" + +FNetworkObjectList& UNetDriver::GetNetworkObjectList() +{ + return *(*(TSharedPtr*)(__int64(this) + Offsets::NetworkObjectList)); +} + +void UNetDriver::RemoveNetworkActor(AActor* Actor) +{ + GetNetworkObjectList().Remove(Actor); + + // RenamedStartupActors.Remove(Actor->GetFName()); +} void UNetDriver::TickFlushHook(UNetDriver* NetDriver) { @@ -32,209 +37,12 @@ void UNetDriver::TickFlushHook(UNetDriver* NetDriver) 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); - // AFortPlayerControllerAthena::GetPlayerViewPointHook((AFortPlayerControllerAthena*)ViewingController, newViewer.ViewLocation, ViewRotation); - newViewer.ViewDir = ViewRotation.Vector(); - } - - return newViewer; -} - -FNetworkObjectList& UNetDriver::GetNetworkObjectList() -{ - return *(*(TSharedPtr*)(__int64(this) + Offsets::NetworkObjectList)); -} - -struct FPacketIdRange -{ - FPacketIdRange(int32 _First, int32 _Last) : First(_First), Last(_Last) { } - FPacketIdRange(int32 PacketId) : First(PacketId), Last(PacketId) { } - // FPacketIdRange() : First(INDEX_NONE), Last(INDEX_NONE) { } - int32 First; - int32 Last; - - bool InRange(int32 PacketId) - { - return (First <= PacketId && PacketId <= Last); - } -}; - -float GetTimeSecondsForWorld(UWorld* World) -{ - static auto TimeSecondsOffset = 0x900; - return *(float*)(__int64(World) + TimeSecondsOffset); -} - -template -T* AllocForReplication(size_t Size) -{ - return (T*)FMemory::Realloc(nullptr, Size, 0); - return Alloc(Size); -} - -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 = AllocForReplication(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"); - - 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 -#define FARSIGHTTHRESHOLD 8000.f -#define CLOSEPROXIMITYSQUARED (CLOSEPROXIMITY*CLOSEPROXIMITY) -#define NEARSIGHTTHRESHOLDSQUARED (NEARSIGHTTHRESHOLD*NEARSIGHTTHRESHOLD) -#define MEDSIGHTTHRESHOLDSQUARED (MEDSIGHTTHRESHOLD*MEDSIGHTTHRESHOLD) -#define FARSIGHTTHRESHOLDSQUARED (FARSIGHTTHRESHOLD*FARSIGHTTHRESHOLD) - -FActorPriority::FActorPriority(UNetConnection* InConnection, UActorChannel* InChannel, FNetworkObjectInfo* InActorInfo, const std::vector& Viewers, bool bLowBandwidth) - : ActorInfo(InActorInfo), Channel(InChannel), DestructionInfo(NULL) -{ - float Time = Channel ? (InConnection->GetDriver()->GetTime() - Channel->GetLastUpdateTime()) : InConnection->GetDriver()->GetSpawnPrioritySeconds(); - // take the highest priority of the viewers on this connection - Priority = 0; - - for (int32 i = 0; i < Viewers.size(); i++) - { - static auto GetNetPriorityOffset = 0x380; - float (*GetNetPriorityOriginal)(AActor* Actor, const FVector& ViewPos, const FVector& ViewDir, AActor* Viewer, AActor* ViewTarget, UActorChannel* InChannel, float Time, bool bLowBandwidth) - = decltype(GetNetPriorityOriginal)(ActorInfo->Actor->VFTable[GetNetPriorityOffset / 8]); - - Priority = FMath::Max(Priority, FMath::RoundToInt(65536.0f * GetNetPriorityOriginal(ActorInfo->Actor, Viewers[i].ViewLocation, Viewers[i].ViewDir, Viewers[i].InViewer, Viewers[i].ViewTarget, InChannel, Time, bLowBandwidth))); - } -} - -FActorPriority::FActorPriority(UNetConnection* InConnection, FActorDestructionInfo* Info, const std::vector& Viewers) - : ActorInfo(NULL), Channel(NULL), DestructionInfo(Info) -{ - Priority = 0; - - for (int32 i = 0; i < Viewers.size(); i++) - { - float Time = InConnection->GetDriver()->GetSpawnPrioritySeconds(); - - FVector Dir = DestructionInfo->DestroyedPosition - Viewers[i].ViewLocation; - float DistSq = Dir.SizeSquared(); - - // adjust priority based on distance and whether actor is in front of viewer - if ((Viewers.at(i).ViewDir | Dir) < 0.f) - { - if (DistSq > NEARSIGHTTHRESHOLDSQUARED) - Time *= 0.2f; - else if (DistSq > CLOSEPROXIMITYSQUARED) - Time *= 0.4f; - } - else if (DistSq > MEDSIGHTTHRESHOLDSQUARED) - Time *= 0.4f; - - Priority = FMath::Max(Priority, 65536.0f * Time); - } -} - -TSet& GetConnectionDestroyedStartupOrDormantActors(UNetConnection* Connection) -{ - return *(TSet*)(__int64(Connection) + 0x33678); -} - -TMap& GetDriverDestroyedStartupOrDormantActors(UNetDriver* NetDriver) -{ - return *(TMap*)(__int64(NetDriver) + 0x228); -} - -void UNetDriver::RemoveNetworkActor(AActor* Actor) -{ - static void (*FNetworkObjectList_Remove)(FNetworkObjectList*, AActor* const a2) = decltype(FNetworkObjectList_Remove)(__int64(GetModuleHandleW(0)) + 0x1AEBB40); - FNetworkObjectList_Remove(&GetNetworkObjectList(), Actor); - // GetNetworkObjectList().Remove(Actor); - - // RenamedStartupActors.Remove(Actor->GetFName()); -} - -int MaxConnectionsToTickPerServerFrame = 25; - int32 ServerReplicateActors_PrepConnections(UNetDriver* NetDriver) { auto& ClientConnections = NetDriver->GetClientConnections(); int32 NumClientsToTick = ClientConnections.Num(); - if (MaxConnectionsToTickPerServerFrame > 0) - NumClientsToTick = FMath::Min(ClientConnections.Num(), MaxConnectionsToTickPerServerFrame); - bool bFoundReadyConnection = false; for (int32 ConnIdx = 0; ConnIdx < ClientConnections.Num(); ConnIdx++) @@ -246,10 +54,8 @@ int32 ServerReplicateActors_PrepConnections(UNetDriver* NetDriver) AActor* OwningActor = Connection->GetOwningActor(); - if (OwningActor != NULL && /* Connection->State == USOCK_Open && */ (Connection->GetDriver()->GetTime() - Connection->GetLastReceiveTime() < 1.5f)) + if (OwningActor != NULL) // && /* Connection->State == USOCK_Open && */ (Connection->Driver->Time - Connection->LastReceiveTime < 1.5f)) { - // check( World == OwningActor->GetWorld() ); - bFoundReadyConnection = true; AActor* DesiredViewTarget = OwningActor; @@ -284,209 +90,14 @@ 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; - union { float f; int32 i; } Result; - union { float f; int32 i; } Temp; - const float SRandTemp = 1.0f; - Temp.f = SRandTemp; - Result.i = (Temp.i & 0xff800000) | (GSRandSeed & 0x007fffff); - auto res = FPlatformMath::Fractional(Result.f); - // MILXNOR - // 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(); - - 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 World = GetWorld(); - - for (const TSharedPtr& ActorInfo : GetNetworkObjectList().ActiveNetworkObjects) - { - if (!ActorInfo->bPendingNetUpdate && GetTimeSecondsForWorld(GetWorld()) <= ActorInfo->NextUpdateTime) - { - continue; - } - - auto Actor = ActorInfo->Actor; - - if (Actor->IsPendingKillPending()) - { - ActorsToRemove.push_back(Actor); - continue; - } - - static auto RemoteRoleOffset = Actor->GetOffset("RemoteRole"); - - if (Actor->Get(RemoteRoleOffset) == ENetRole::ROLE_None) - { - ActorsToRemove.push_back(Actor); - continue; - } - - // We should add a NetDriverName check but I don't believe it is needed. - - // We should check if the actor is initialized here. - - // We should check the level stuff here. - - if (Actor->GetNetDormancy() == ENetDormancy::DORM_Initial && Actor->IsNetStartupActor()) - { - ActorsToRemove.push_back(Actor); - continue; - } - - static auto NeedsLoadForClientOriginalOffset = 0xD0; - bool (*NeedsLoadForClientOriginal)(AActor* Actor) = decltype(NeedsLoadForClientOriginal)(Actor->VFTable[NeedsLoadForClientOriginalOffset / 8]); - - 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 = GetTimeSecondsForWorld(World); - ActorInfo->OptimalNetUpdateDelta = 1.0f / Actor->GetNetUpdateFrequency(); - } - - const float ScaleDownStartTime = 2.0f; - const float ScaleDownTimeRange = 5.0f; - - const float LastReplicateDelta = GetTimeSecondsForWorld(World) - ActorInfo->LastNetReplicateTime; - - if (LastReplicateDelta > ScaleDownStartTime) - { - if (Actor->GetMinNetUpdateFrequency() == 0.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->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); - } - - if (!ActorInfo->bPendingNetUpdate) - { - constexpr bool bUseAdapativeNetFrequency = false; - const float NextUpdateDelta = bUseAdapativeNetFrequency ? ActorInfo->OptimalNetUpdateDelta : 1.0f / Actor->GetNetUpdateFrequency(); - - // then set the next update time - 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); - CallPreReplication(Actor, this); - } - - for (auto Actor : ActorsToRemove) - { - if (!Actor) - continue; - - RemoveNetworkActor(Actor); - } -} - -static bool IsActorRelevantToConnection(AActor* Actor, const std::vector& ConnectionViewers) -{ - for (int32 viewerIdx = 0; viewerIdx < ConnectionViewers.size(); viewerIdx++) - { - if (!ConnectionViewers[viewerIdx].ViewTarget) - continue; - - // static bool (*IsNetRelevantFor)(AActor*, AActor*, AActor*, FVector&) = decltype(IsNetRelevantFor)(__int64(GetModuleHandleW(0)) + 0x1ECC700); - - static auto index = Offsets::IsNetRelevantFor; - - // if (Actor->IsNetRelevantFor(ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) - // if (IsNetRelevantFor(Actor, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) - if (reinterpret_cast(Actor->VFTable[index])( - Actor, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) - { - return true; - } - } - - return false; -} - -bool UNetDriver::IsLevelInitializedForActor(const AActor* InActor, const UNetConnection* InConnection) const -{ - const bool bCorrectWorld = (InConnection->GetClientWorldPackageName() == GetWorldPackage()->NamePrivate && InConnection->ClientHasInitializedLevelFor(InActor)); - const bool bIsConnectionPC = (InActor == InConnection->GetPlayerController()); - return bCorrectWorld || bIsConnectionPC; -} - -static FORCEINLINE bool IsActorDormant(FNetworkObjectInfo* ActorInfo, const TWeakObjectPtr& Connection) -{ - // If actor is already dormant on this channel, then skip replication entirely - return ActorInfo->DormantConnections.Contains(Connection); -} - -static FORCEINLINE UNetConnection* IsActorOwnedByAndRelevantToConnection(const AActor* Actor, const std::vector& ConnectionViewers, bool& bOutHasNullViewTarget) -{ - const AActor* ActorOwner = Actor->GetNetOwner(); - - bOutHasNullViewTarget = false; - - for (int i = 0; i < ConnectionViewers.size(); i++) - { - UNetConnection* ViewerConnection = ConnectionViewers[i].Connection; - - if (ViewerConnection->GetViewTarget() == nullptr) - { - bOutHasNullViewTarget = true; - } - - if (ActorOwner == ViewerConnection->GetPlayerController() || - (ViewerConnection->GetPlayerController() && ActorOwner == ViewerConnection->GetPlayerController()->GetPawn()) || - (ViewerConnection->GetViewTarget() && ViewerConnection->GetViewTarget()->IsRelevancyOwnerFor(Actor, ActorOwner, ViewerConnection->GetOwningActor()))) - { - return ViewerConnection; - } - } - - return nullptr; -} - -struct FCompareFActorPriority -{ - FORCEINLINE bool operator()(const FActorPriority& A, const FActorPriority& B) const - { - return B.Priority < A.Priority; - } -}; - static FORCEINLINE bool ShouldActorGoDormant(AActor* Actor, const std::vector& ConnectionViewers, UActorChannel* Channel, const float Time, const bool bLowNetBandwidth) { using enum ENetDormancy; @@ -496,7 +107,7 @@ static FORCEINLINE bool ShouldActorGoDormant(AActor* Actor, const std::vectorGetNetDormancy() <= DORM_Awake || !Channel + if (Actor->GetNetDormancy() <= DORM_Awake || !Channel // || ReadBitfield((PlaceholderBitfield*)(__int64(Channel) + bPendingDormancyOffset), bPendingDormancyFieldMask) // || ReadBitfield((PlaceholderBitfield*)(__int64(Channel) + DormantOffset), DormantFieldMask) || Channel->IsPendingDormancy() @@ -522,28 +133,117 @@ static FORCEINLINE bool ShouldActorGoDormant(AActor* Actor, const std::vector& OutConsiderList, float ServerTickTime) { - if (true) - { - auto& ActorChannels = Connection->GetActorChannels(); + std::vector ActorsToRemove; - for (auto& Pair : ActorChannels) + auto& ActiveObjects = GetNetworkObjectList().ActiveNetworkObjects; + + auto World = GetWorld(); + + for (const TSharedPtr& ActorInfo : ActiveObjects) + { + if (!ActorInfo->bPendingNetUpdate && UGameplayStatics::GetTimeSeconds(GetWorld()) <= ActorInfo->NextUpdateTime) { - if (Pair.First == ActorInfo->WeakActor) - { - return Pair.Second; - } + continue; } - return nullptr; + auto Actor = ActorInfo->Actor; + + if (!Actor) + continue; + + if (Actor->IsPendingKillPending()) + // if (Actor->IsPendingKill()) + { + ActorsToRemove.push_back(Actor); + continue; + } + + static auto RemoteRoleOffset = Actor->GetOffset("RemoteRole"); + + if (Actor->Get(RemoteRoleOffset) == ENetRole::ROLE_None) + { + ActorsToRemove.push_back(Actor); + continue; + } + + // We should add a NetDriverName check but I don't believe it is needed. + + // We should check if the actor is initialized here. + + // We should check the level stuff here. + + static auto NetDormancyOffset = Actor->GetOffset("NetDormancy"); + + if (Actor->Get(NetDormancyOffset) == ENetDormancy::DORM_Initial && Actor->IsNetStartupActor()) // IsDormInitialStartupActor + { + continue; + } + + // We should check NeedsLoadForClient here. + // We should make sure the actor is in the same world here but I don't believe it is needed. + + if (ActorInfo->LastNetReplicateTime == 0) + { + ActorInfo->LastNetReplicateTime = UGameplayStatics::GetTimeSeconds(World); + ActorInfo->OptimalNetUpdateDelta = 1.0f / Actor->GetNetUpdateFrequency(); + } + + const float ScaleDownStartTime = 2.0f; + const float ScaleDownTimeRange = 5.0f; + + const float LastReplicateDelta = UGameplayStatics::GetTimeSeconds(World) - ActorInfo->LastNetReplicateTime; + + if (LastReplicateDelta > ScaleDownStartTime) + { + static auto MinNetUpdateFrequencyOffset = Actor->GetOffset("MinNetUpdateFrequency"); + + if (Actor->Get(MinNetUpdateFrequencyOffset) == 0.0f) + { + Actor->Get(MinNetUpdateFrequencyOffset) = 2.0f; + } + + const float MinOptimalDelta = 1.0f / Actor->GetNetUpdateFrequency(); // Don't go faster than NetUpdateFrequency + const float MaxOptimalDelta = max(1.0f / Actor->GetMinNetUpdateFrequency(), MinOptimalDelta); // Don't go slower than MinNetUpdateFrequency (or NetUpdateFrequency if it's slower) + + const float Alpha = std::clamp((LastReplicateDelta - ScaleDownStartTime) / ScaleDownTimeRange, 0.0f, 1.0f); // should we use fmath? + ActorInfo->OptimalNetUpdateDelta = std::lerp(MinOptimalDelta, MaxOptimalDelta, Alpha); // should we use fmath? + } + + if (!ActorInfo->bPendingNetUpdate) + { + constexpr bool bUseAdapativeNetFrequency = false; + const float NextUpdateDelta = bUseAdapativeNetFrequency ? ActorInfo->OptimalNetUpdateDelta : 1.0f / Actor->GetNetUpdateFrequency(); + + // then set the next update time + float ServerTickTime = 1.f / 30; + ActorInfo->NextUpdateTime = UGameplayStatics::GetTimeSeconds(World) + FRand() * ServerTickTime + NextUpdateDelta; + static auto TimeOffset = GetOffset("Time"); + ActorInfo->LastNetUpdateTime = Get(TimeOffset); + } + + ActorInfo->bPendingNetUpdate = false; + + OutConsiderList.push_back(ActorInfo.Get()); + + static void (*CallPreReplication)(AActor*, UNetDriver*) = decltype(CallPreReplication)(Addresses::CallPreReplication); + CallPreReplication(Actor, this); } - auto Actor = ActorInfo->Actor; + for (auto Actor : ActorsToRemove) + { + if (!Actor) + continue; - if (!Actor) - return nullptr; + /* LOG_INFO(LogDev, "Removing actor: {}", Actor ? Actor->GetFullName() : "InvalidObject"); + RemoveNetworkActor(Actor); + LOG_INFO(LogDev, "Finished removing actor."); */ + } +} +static UActorChannel* FindChannel(AActor * Actor, UNetConnection * Connection) +{ static auto OpenChannelsOffset = Connection->GetOffset("OpenChannels"); auto& OpenChannels = Connection->Get>(OpenChannelsOffset); @@ -560,15 +260,15 @@ UActorChannel* FindChannel(UNetConnection* Connection, FNetworkObjectInfo* Actor // LOG_INFO(LogReplication, "[{}] Class {}", i, Channel->ClassPrivate ? Channel->ClassPrivate->GetFullName() : "InvalidObject"); - // if (!Channel->IsA(ActorChannelClass)) - // continue; - - if (Channel->ClassPrivate != ActorChannelClass) + if (!Channel->IsA(ActorChannelClass)) // (Channel->ClassPrivate == ActorChannelClass) continue; static auto ActorOffset = Channel->GetOffset("Actor"); + auto ChannelActor = Channel->Get(ActorOffset); - if (Channel->Get(ActorOffset) != Actor) + // LOG_INFO(LogReplication, "[{}] {}", i, ChannelActor->GetFullName()); + + if (ChannelActor != Actor) continue; return (UActorChannel*)Channel; @@ -577,319 +277,71 @@ UActorChannel* FindChannel(UNetConnection* Connection, FNetworkObjectInfo* Actor return NULL; } -int32 UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection* Connection, const std::vector& ConnectionViewers, const std::vector ConsiderList, const bool bCPUSaturated, FActorPriority*& OutPriorityList, FActorPriority**& OutPriorityActors) +static bool IsActorRelevantToConnection(AActor * Actor, std::vector&ConnectionViewers) { - GetNetTag()++; - Connection->GetTickCount()++; - - for (int32 j = 0; j < Connection->GetSentTemporaries().Num(); j++) // Set up to skip all sent temporary actors + for (int32 viewerIdx = 0; viewerIdx < ConnectionViewers.size(); viewerIdx++) { - Connection->GetSentTemporaries().at(j)->GetNetTag() = GetNetTag(); - } + if (!ConnectionViewers[viewerIdx].ViewTarget) + continue; - // check( World == Connection->OwningActor->GetWorld() ); + // static bool (*IsNetRelevantFor)(AActor*, AActor*, AActor*, FVector&) = decltype(IsNetRelevantFor)(__int64(GetModuleHandleW(0)) + 0x1ECC700); - int32 FinalSortedCount = 0; - int32 DeletedCount = 0; + static auto index = Offsets::IsNetRelevantFor; - // Make weak ptr once for IsActorDormant call - TWeakObjectPtr WeakConnection{}; - WeakConnection.ObjectIndex = Connection->InternalIndex; - WeakConnection.ObjectSerialNumber = GetItemByIndex(Connection->InternalIndex)->SerialNumber; - - auto& Connection_DestroyedStartupOrDormantActors = GetConnectionDestroyedStartupOrDormantActors(Connection); - - const int32 MaxSortedActors = ConsiderList.size() + Connection_DestroyedStartupOrDormantActors.Num(); - - if (MaxSortedActors > 0) - { - OutPriorityList = Alloc(MaxSortedActors * sizeof(FActorPriority)); - // (FActorPriority*)FMemory::Realloc(nullptr, MaxSortedActors * sizeof(FActorPriority), 0); - OutPriorityActors = Alloc(MaxSortedActors * sizeof(FActorPriority*)); - // (FActorPriority**)FMemory::Realloc(nullptr, MaxSortedActors * sizeof(FActorPriority*), 0); - - // check( World == Connection->ViewTarget->GetWorld() ); - - // AGameNetworkManager* const NetworkManager = World->NetworkManager; - const bool bLowNetBandwidth = false; // NetworkManager ? NetworkManager->IsInLowBandwidthMode() : false; - - for (int i = 0; i < ConsiderList.size(); i++) + // if (Actor->IsNetRelevantFor(ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) + // if (IsNetRelevantFor(Actor, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) + if (reinterpret_cast(Actor->VFTable[index])( + Actor, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) { - FNetworkObjectInfo* ActorInfo = ConsiderList.at(i); - AActor* Actor = ActorInfo->Actor; - - UActorChannel* Channel = FindChannel(Connection, ActorInfo); - - if (!Channel) - { - if (!IsLevelInitializedForActor(Actor, Connection)) - { - // If the level this actor belongs to isn't loaded on client, don't bother sending - continue; - } - - if (!IsActorRelevantToConnection(Actor, ConnectionViewers)) - { - // If not relevant (and we don't have a channel), skip - continue; - } - } - - // LOG_INFO(LogDev, "Actor->GetNetTag(): {}", Actor->GetNetTag()); - - UNetConnection* PriorityConnection = Connection; - - if (Actor->IsOnlyRelevantToOwner()) - { - // This actor should be owned by a particular connection, see if that connection is the one passed in - bool bHasNullViewTarget = false; - - PriorityConnection = IsActorOwnedByAndRelevantToConnection(Actor, ConnectionViewers, bHasNullViewTarget); - - if (PriorityConnection == nullptr) - { - if (!bHasNullViewTarget && Channel != NULL && GetTime() - Channel->GetRelevantTime() >= GetRelevantTimeout()) - { - Channel->Close(); - } - - continue; - } - } - else if (CVarSetNetDormancyEnabled != 0) - { - // Skip Actor if dormant - if (IsActorDormant(ActorInfo, WeakConnection)) - { - continue; - } - - // See of actor wants to try and go dormant - if (ShouldActorGoDormant(Actor, ConnectionViewers, Channel, GetTime(), bLowNetBandwidth)) - { - // LOG_INFO(LogDev, "Actor is going dormant!"); - - // Channel is marked to go dormant now once all properties have been replicated (but is not dormant yet) - Channel->StartBecomingDormant(); - } - } - - // Actor is relevant to this connection, add it to the list - // NOTE - We use NetTag to make sure SentTemporaries didn't already mark this actor to be skipped - - if (Actor->GetNetTag() != GetNetTag()) - { - Actor->GetNetTag() = GetNetTag(); - - OutPriorityList[FinalSortedCount] = FActorPriority(PriorityConnection, Channel, ActorInfo, ConnectionViewers, bLowNetBandwidth); - OutPriorityActors[FinalSortedCount] = OutPriorityList + FinalSortedCount; - - FinalSortedCount++; - // Test3(Actor, "ryo i got added"); - } + return true; } - - // Add in deleted actors - - /* for (auto& CurrentGuid : Connection_DestroyedStartupOrDormantActors) - { - bool bFound = false; - - FActorDestructionInfo& DInfo = GetDriverDestroyedStartupOrDormantActors(this).Find(CurrentGuid, &bFound); - - if (!bFound) - continue; - - OutPriorityList[FinalSortedCount] = FActorPriority(Connection, &DInfo, ConnectionViewers); - OutPriorityActors[FinalSortedCount] = OutPriorityList + FinalSortedCount; - FinalSortedCount++; - DeletedCount++; - } */ - - Sort(OutPriorityActors, FinalSortedCount, FCompareFActorPriority()); } - return FinalSortedCount; + return false; } -int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* Connection, const std::vector& ConnectionViewers, FActorPriority** PriorityActors, const int32 FinalSortedCount, int32& OutUpdated) +static FNetViewer ConstructNetViewer(UNetConnection * NetConnection) { - static UChannel* (*CreateChannel)(UNetConnection*, int, bool, int32_t) = decltype(CreateChannel)(Addresses::CreateChannel); - static __int64 (*ReplicateActor)(UActorChannel*) = decltype(ReplicateActor)(Addresses::ReplicateActor); - static __int64 (*SetChannelActor)(UActorChannel*, AActor*) = decltype(SetChannelActor)(Addresses::SetChannelActor); + FNetViewer newViewer{}; + newViewer.Connection = NetConnection; + newViewer.InViewer = NetConnection->GetPlayerController() ? NetConnection->GetPlayerController() : NetConnection->GetOwningActor(); + newViewer.ViewTarget = NetConnection->GetViewTarget(); - int32 ActorUpdatesThisConnection = 0; - int32 ActorUpdatesThisConnectionSent = 0; - int32 FinalRelevantCount = 0; + if (!NetConnection->GetOwningActor() || !(!NetConnection->GetPlayerController() || (NetConnection->GetPlayerController() == NetConnection->GetOwningActor()))) + return newViewer; - if (!Connection->IsNetReady(0)) + APlayerController* ViewingController = NetConnection->GetPlayerController(); + + newViewer.ViewLocation = newViewer.ViewTarget->GetActorLocation(); + + if (ViewingController) { - return 0; + static auto ControlRotationOffset = ViewingController->GetOffset("ControlRotation"); + FRotator ViewRotation = ViewingController->Get(ControlRotationOffset); // hmmmm // ViewingController->GetControlRotation(); + // AFortPlayerControllerAthena::GetPlayerViewPointHook(Cast(ViewingController, false), newViewer.ViewLocation, ViewRotation); + // ViewingController->GetActorEyesViewPoint(&newViewer.ViewLocation, &ViewRotation); // HMMM + + static auto GetActorEyesViewPointOffset = 0x5B0; + void (*GetActorEyesViewPointOriginal)(AController*, FVector * a2, FRotator * a3) = decltype(GetActorEyesViewPointOriginal)(ViewingController->VFTable[GetActorEyesViewPointOffset / 8]); + GetActorEyesViewPointOriginal(ViewingController, &newViewer.ViewLocation, &ViewRotation); + // AFortPlayerControllerAthena::GetPlayerViewPointHook((AFortPlayerControllerAthena*)ViewingController, newViewer.ViewLocation, ViewRotation); + newViewer.ViewDir = ViewRotation.Vector(); } - for (int32 j = 0; j < FinalSortedCount; j++) - { - FNetworkObjectInfo* ActorInfo = PriorityActors[j]->ActorInfo; + return newViewer; +} - // Deletion entry - if (ActorInfo == NULL && PriorityActors[j]->DestructionInfo) - { - // Make sure client has streaming level loaded - if (PriorityActors[j]->DestructionInfo->StreamingLevelName.ComparisonIndex.Value != NAME_None - && !Connection->GetClientVisibleLevelNames().Contains(PriorityActors[j]->DestructionInfo->StreamingLevelName) - ) - { - // This deletion entry is for an actor in a streaming level the connection doesn't have loaded, so skip it - continue; - } +static FORCEINLINE bool IsActorDormant(FNetworkObjectInfo* ActorInfo, const TWeakObjectPtr& Connection) +{ + // If actor is already dormant on this channel, then skip replication entirely + return ActorInfo->DormantConnections.Contains(Connection); +} - 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++; - - 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) - LOG_INFO(LogDev, "Finished!"); - } - - continue; - } - - // Normal actor replication - UActorChannel* Channel = PriorityActors[j]->Channel; - - if (!Channel || Channel->GetActor()) //make sure didn't just close this channel - { - AActor* Actor = ActorInfo->Actor; - // Test2(Actor, "Before"); - - bool bIsRelevant = false; - - const bool bLevelInitializedForActor = IsLevelInitializedForActor(Actor, Connection); - - // only check visibility on already visible actors every 1.0 + 0.5R seconds - // bTearOff actors should never be checked - if (bLevelInitializedForActor) - { - if (!Actor->IsTearOff() && (!Channel || GetTime() - Channel->GetRelevantTime() > 1.f)) - { - if (IsActorRelevantToConnection(Actor, ConnectionViewers)) - { - bIsRelevant = true; - } - } - } - - // if the actor is now relevant or was recently relevant - const bool bIsRecentlyRelevant = bIsRelevant || (Channel && GetTime() - Channel->GetRelevantTime() < GetRelevantTimeout()) || ActorInfo->bForceRelevantNextUpdate; - - // 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)); - - ActorInfo->bForceRelevantNextUpdate = false; - - if (bIsRecentlyRelevant) - { - FinalRelevantCount++; - - if (Channel == NULL - /* && GetGuidCache()->SupportsObject(Actor->ClassPrivate) - && GetGuidCache()->SupportsObject(Actor->IsNetStartupActor() ? Actor : Actor->GetArchetype()) */ - ) - { - if (bLevelInitializedForActor) - { - // Create a new channel for this actor. - UActorChannel* Channel = (UActorChannel*)CreateChannel(Connection, 2, true, -1); - - if (Channel) - { - SetChannelActor(Channel, Actor); - } - } - // 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 = GetTimeSecondsForWorld(GetWorld()); // Actor->GetWorld()->TimeSeconds - ActorInfo->NextUpdateTime = TimeSeconds + 0.2f * FRand(); - } - } - - if (Channel) - { - // 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 (Channel->IsNetReady(0)) - { - 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 = (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 = GetTimeSecondsForWorld(GetWorld()); - // ReplicatedActors.emplace(Actor->GetFullName()); - } - - ActorUpdatesThisConnection++; - OutUpdated++; - } - else - { - Actor->ForceNetUpdate(); - } - - if (!Connection->IsNetReady(0)) - { - return j; - } - } - } - - if ((!bIsRecentlyRelevant || Actor->IsTearOff()) && Channel != NULL) - { - if (!bLevelInitializedForActor || !Actor->IsNetStartupActor()) - { - Channel->Close(); - } - } - } - } - - // LOG_INFO(LogDev, "FinalRelevantCount: {} ActorUpdatesThisConnection: {} ActorUpdatesThisConnectionSent: {}", FinalRelevantCount, ActorUpdatesThisConnection, ActorUpdatesThisConnectionSent); - - return FinalSortedCount; +bool UNetDriver::IsLevelInitializedForActor(const AActor* InActor, const UNetConnection* InConnection) const +{ + const bool bCorrectWorld = (InConnection->GetClientWorldPackageName() == GetWorldPackage()->NamePrivate && InConnection->ClientHasInitializedLevelFor(InActor)); + const bool bIsConnectionPC = (InActor == InConnection->GetPlayerController()); + return bCorrectWorld || bIsConnectionPC; } int32 UNetDriver::ServerReplicateActors() @@ -906,17 +358,15 @@ int32 UNetDriver::ServerReplicateActors() return 0; } - auto World = GetNetDriverWorld(); - - AWorldSettings* WorldSettings = World->GetWorldSettings(); + // AFortWorldSettings* WorldSettings = GetFortWorldSettings(NetDriver->World); // bool bCPUSaturated = false; float ServerTickTime = GetMaxTickRateHook(); - if (ServerTickTime == 0.f) + /* if (ServerTickTime == 0.f) { - // ServerTickTime = DeltaSeconds; + ServerTickTime = DeltaSeconds; } - else + else */ { ServerTickTime = 1.f / ServerTickTime; // bCPUSaturated = DeltaSeconds > 1.2f * ServerTickTime; @@ -928,133 +378,166 @@ int32 UNetDriver::ServerReplicateActors() // std::cout << "ConsiderList.size(): " << GetNetworkObjectList(NetDriver).ActiveNetworkObjects.Num() << '\n'; + auto World = GetWorld(); + ServerReplicateActors_BuildConsiderList(ConsiderList, ServerTickTime); - bool bCPUSaturated = false; - - auto& ClientConnections = GetClientConnections(); - - for (int32 i = 0; i < ClientConnections.Num(); i++) + for (int32 i = 0; i < this->GetClientConnections().Num(); i++) { - UNetConnection* Connection = ClientConnections.at(i); + UNetConnection* Connection = this->GetClientConnections().at(i); if (!Connection) continue; - // 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 (i >= NumClientsToTick) + continue; - if (Replicator.OwningChannel != nullptr) - { - Replicator.ValidateAgainstState(Replicator.OwningChannel->GetActor()); - } - } + if (!Connection->GetViewTarget()) + continue; + + if (Connection->GetPlayerController()) + { + static void (*SendClientAdjustment)(APlayerController*) = decltype(SendClientAdjustment)(Addresses::SendClientAdjustment); + SendClientAdjustment(Connection->GetPlayerController()); + } + + // Make weak ptr once for IsActorDormant call + TWeakObjectPtr WeakConnection{}; + WeakConnection.ObjectIndex = Connection->InternalIndex; + WeakConnection.ObjectSerialNumber = GetItemByIndex(Connection->InternalIndex)->SerialNumber; + + /* GetNetTag()++; + Connection->GetTickCount()++; + + for (int32 j = 0; j < Connection->GetSentTemporaries().Num(); j++) // Set up to skip all sent temporary actors + { + Connection->GetSentTemporaries().at(j)->GetNetTag() = GetNetTag(); } */ - // if this client shouldn't be ticked this frame - if (i >= NumClientsToTick) + for (auto& ActorInfo : ConsiderList) { - //UE_LOG(LogNet, Log, TEXT("skipping update to %s"),*Connection->GetName()); - // then mark each considered actor as bPendingNetUpdate so that they will be considered again the next frame when the connection is actually ticked - for (int32 ConsiderIdx = 0; ConsiderIdx < ConsiderList.size(); ConsiderIdx++) + if (!ActorInfo || !ActorInfo->Actor) + continue; + + auto Actor = ActorInfo->Actor; + + auto Channel = FindChannel(Actor, Connection); + + /* if (IsActorDormant(ActorInfo, WeakConnection)) { - AActor* Actor = ConsiderList[ConsiderIdx]->Actor; - // if the actor hasn't already been flagged by another connection, - if (Actor != NULL && !ConsiderList[ConsiderIdx]->bPendingNetUpdate) - { - // find the channel + continue; + } */ - 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) - { - ConsiderList[ConsiderIdx]->bPendingNetUpdate = true; - } - } - } - - static auto TimeSensitiveOffset = 0x241; - Connection->Get(TimeSensitiveOffset) = false; - } - else if (Connection->GetViewTarget()) - { - // Make a list of viewers this connection should consider (this connection and children of this connection) - // TArray& ConnectionViewers = WorldSettings->ReplicationViewers; - - // ConnectionViewers.Reset(); std::vector ConnectionViewers; ConnectionViewers.push_back(ConstructNetViewer(Connection)); - // send ClientAdjustment if necessary - // we do this here so that we send a maximum of one per packet to that client; there is no value in stacking additional corrections - if (Connection->GetPlayerController()) + if (!Channel) { - static void (*SendClientAdjustment)(APlayerController*) = decltype(SendClientAdjustment)(Addresses::SendClientAdjustment); - SendClientAdjustment(Connection->GetPlayerController()); - } - - FActorPriority* PriorityList = NULL; - FActorPriority** PriorityActors = NULL; - - // LOG_INFO(LogDev, "ConsiderList.size(): {}", ConsiderList.size()); - - // Get a sorted list of actors for this connection - const int32 FinalSortedCount = ServerReplicateActors_PrioritizeActors(Connection, ConnectionViewers, ConsiderList, bCPUSaturated, PriorityList, PriorityActors); - - // Process the sorted list of actors for this connection - const int32 LastProcessedActor = ServerReplicateActors_ProcessPrioritizedActors(Connection, ConnectionViewers, PriorityActors, FinalSortedCount, Updated); - - // LOG_INFO(LogDev, "LastProcessedActor: {} FinalSortedCount: {} NetTag: {}", LastProcessedActor, FinalSortedCount, GetNetTag()); - - // relevant actors that could not be processed this frame are marked to be considered for next frame - for (int32 k = LastProcessedActor; k < FinalSortedCount; k++) - { - if (!PriorityActors[k]->ActorInfo) + if (!IsLevelInitializedForActor(Actor, Connection)) { - // A deletion entry, skip it because we dont have anywhere to store a 'better give higher priority next time' + // If the level this actor belongs to isn't loaded on client, don't bother sending continue; } - AActor* Actor = PriorityActors[k]->ActorInfo->Actor; - - UActorChannel* Channel = PriorityActors[k]->Channel; - - if (Channel != NULL && GetTime() - Channel->GetRelevantTime() <= 1.f) + /* if (!IsActorRelevantToConnection(Actor, ConnectionViewers)) { - PriorityActors[k]->ActorInfo->bPendingNetUpdate = true; - } - else if (IsActorRelevantToConnection(Actor, ConnectionViewers)) + // If not relevant (and we don't have a channel), skip + continue; + } */ + } + + bool bLowNetBandwidth = false; + + // See of actor wants to try and go dormant + /* if (ShouldActorGoDormant(Actor, ConnectionViewers, Channel, GetTime(), bLowNetBandwidth)) + { + // LOG_INFO(LogDev, "Actor is going dormant!"); + + // Channel is marked to go dormant now once all properties have been replicated (but is not dormant yet) + Channel->StartBecomingDormant(); + } */ + + if (Addresses::ActorChannelClose && Offsets::IsNetRelevantFor) + { + static void (*ActorChannelClose)(UActorChannel*) = decltype(ActorChannelClose)(Addresses::ActorChannelClose); + + if (!Actor->IsAlwaysRelevant() && !Actor->UsesOwnerRelevancy() && !Actor->IsOnlyRelevantToOwner()) { - PriorityActors[k]->ActorInfo->bPendingNetUpdate = true; - if (Channel != NULL) + if (Connection && Connection->GetViewTarget()) { - Channel->GetRelevantTime() = GetTime() + 0.5f * SRand(); + auto Viewer = Connection->GetViewTarget(); + auto Loc = Viewer->GetActorLocation(); + + if (!IsActorRelevantToConnection(Actor, ConnectionViewers)) + { + if (Channel) + ActorChannelClose(Channel); + + continue; + } } } } - ConnectionViewers.clear(); + static UChannel* (*CreateChannel)(UNetConnection*, int, bool, int32_t) = decltype(CreateChannel)(Addresses::CreateChannel); + static __int64 (*ReplicateActor)(UActorChannel*) = decltype(ReplicateActor)(Addresses::ReplicateActor); + static __int64 (*SetChannelActor)(UActorChannel*, AActor*) = decltype(SetChannelActor)(Addresses::SetChannelActor); + + const bool bLevelInitializedForActor = IsLevelInitializedForActor(Actor, Connection); + + if (!Channel) + { + if (Actor->IsA(APlayerController::StaticClass()) && Actor != Connection->GetPlayerController()) // isnetrelevantfor should handle this iirc + continue; + + if (bLevelInitializedForActor) + { + Channel = (UActorChannel*)CreateChannel(Connection, 2, true, -1); + + if (Channel) + { + SetChannelActor(Channel, Actor); + } + } + + else if (Actor->GetNetUpdateFrequency() < 1.0f) + { + ActorInfo->NextUpdateTime = UGameplayStatics::GetTimeSeconds(GetWorld()) + 0.2f * FRand(); + } + } + + if (Channel) + { + if (ReplicateActor(Channel)) + { + 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; + } + } } } // shuffle the list of connections if not all connections were ticked - /* if (NumClientsToTick < ClientConnections.Num()) + /* + if (NumClientsToTick < NetDriver->ClientConnections.Num()) { int32 NumConnectionsToMove = NumClientsToTick; while (NumConnectionsToMove > 0) { // move all the ticked connections to the end of the list so that the other connections are considered first for the next frame - UNetConnection* Connection = ClientConnections.at(0); - ClientConnections.RemoveAt(0, 1); - ClientConnections.Add(Connection); + UNetConnection* Connection = NetDriver->ClientConnections[0]; + NetDriver->ClientConnections.RemoveAt(0, 1); + NetDriver->ClientConnections.Add(Connection); NumConnectionsToMove--; } - } */ + } + */ return Updated; } \ No newline at end of file diff --git a/Project Reboot 3.0/Object.cpp b/Project Reboot 3.0/Object.cpp index 802befd..1c7fbec 100644 --- a/Project Reboot 3.0/Object.cpp +++ b/Project Reboot 3.0/Object.cpp @@ -155,6 +155,23 @@ bool UObject::IsA(UClass* otherClass) return false; } +UFunction* UObject::FindFunction(const std::string& ShortFunctionName) +{ + // We could also loop through children. + + UClass* super = ClassPrivate; + + while (super) + { + if (auto Func = FindObject(super->GetPathName() + "." + ShortFunctionName)) + return Func; + + super = super->GetSuperStruct(); + } + + return nullptr; +} + /* class UClass* UObject::StaticClass() { static auto Class = FindObject("/Script/CoreUObject.Object"); diff --git a/Project Reboot 3.0/Object.h b/Project Reboot 3.0/Object.h index 17bda5a..fd45bc5 100644 --- a/Project Reboot 3.0/Object.h +++ b/Project Reboot 3.0/Object.h @@ -55,6 +55,7 @@ public: std::string GetFullName(); bool IsA(UClass* Other); + class UFunction* FindFunction(const std::string& ShortFunctionName); void* GetProperty(const std::string& ChildName, bool bWarnIfNotFound = true); void* GetProperty(const std::string& ChildName, bool bWarnIfNotFound = true) const; diff --git a/Project Reboot 3.0/ObjectMacros.h b/Project Reboot 3.0/ObjectMacros.h index 3cd4a23..fff169b 100644 --- a/Project Reboot 3.0/ObjectMacros.h +++ b/Project Reboot 3.0/ObjectMacros.h @@ -63,4 +63,7 @@ enum class EInternalObjectFlags : int PRAGMA_ENABLE_DEPRECATION_WARNINGS */ }; -ENUM_CLASS_FLAGS(EInternalObjectFlags) \ No newline at end of file +ENUM_CLASS_FLAGS(EInternalObjectFlags) + +#define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D +#define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D) \ No newline at end of file diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj b/Project Reboot 3.0/Project Reboot 3.0.vcxproj index 809f3e5..5c478a8 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj @@ -1,4 +1,4 @@ - + @@ -192,6 +192,8 @@ + + @@ -257,6 +259,9 @@ + + + @@ -282,6 +287,8 @@ + + @@ -291,8 +298,11 @@ + + + @@ -335,6 +345,8 @@ + + @@ -344,12 +356,14 @@ + + @@ -390,6 +404,7 @@ + @@ -402,9 +417,11 @@ + + @@ -420,6 +437,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 c8d4e09..50f2774 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters @@ -239,6 +239,12 @@ FortniteGame\Source\FortniteGame\Private\Mutators + + FortniteGame\Source\FortniteGame\Private\Mutators + + + FortniteGame\Source\FortniteGame\Private\Mutators + @@ -746,6 +752,51 @@ FortniteGame\Source\FortniteGame\Public\Mutators + + FortniteGame\Source\FortniteGame\Public\Mutators + + + FortniteGame\Source\FortniteGame\Public\Mutators + + + FortniteGame\Source\FortniteGame\Public\Building\GameplayActors\Barrier + + + FortniteGame\Source\FortniteGame\Public\Building\GameplayActors\Barrier + + + FortniteGame\Source\FortniteGame\Public\Mutators + + + FortniteGame\Source\FortniteGame\Public\Building\GameplayActors\Barrier + + + Engine\Source\Runtime\CoreUObject\Public\UObject + + + Engine\Source\Runtime\Engine\Public + + + Engine\Source\Runtime\Core\Public\Delegates + + + Engine\Source\Runtime\CoreUObject\Public\UObject + + + Engine\Source\Runtime\Core\Public\GenericPlatform + + + Engine\Source\Runtime\Core\Public\Templates + + + Engine\Source\Runtime\Engine\Classes\Engine + + + Engine\Source\Runtime\Core\Public\Delegates + + + Engine\Source\Runtime\Core\Public\Templates + @@ -913,9 +964,6 @@ {18a41ed2-b864-4a36-9396-cb0672b8464d} - - {a28fab04-cc3c-4032-ac3e-22c6b9379312} - {7905895d-5ebf-4313-a9de-06f507c35113} @@ -976,6 +1024,15 @@ {6efc7bff-2d7b-4fdb-bae8-3d2e736cc82e} + + {a633ca3f-f699-4c92-8522-e49a14157b95} + + + {a28fab04-cc3c-4032-ac3e-22c6b9379312} + + + {3d909143-220c-44b3-819f-79d282c3fc0f} + @@ -984,5 +1041,8 @@ Engine\Source\Runtime\Core\Public\Delegates + + Engine\Source\Runtime\Core\Public\Misc + \ No newline at end of file diff --git a/Project Reboot 3.0/ScriptDelegates.h b/Project Reboot 3.0/ScriptDelegates.h new file mode 100644 index 0000000..2455b69 --- /dev/null +++ b/Project Reboot 3.0/ScriptDelegates.h @@ -0,0 +1,10 @@ +#pragma once +#include "WeakObjectPtr.h" + +template +class TScriptDelegate +{ +public: + TWeakPtr Object; + FName FunctionName; +}; \ No newline at end of file diff --git a/Project Reboot 3.0/ScriptInterface.h b/Project Reboot 3.0/ScriptInterface.h index cd2fe73..7984d9b 100644 --- a/Project Reboot 3.0/ScriptInterface.h +++ b/Project Reboot 3.0/ScriptInterface.h @@ -5,8 +5,8 @@ class FScriptInterface { public: - UObject* ObjectPointer; - void* InterfacePointer; + UObject* ObjectPointer = nullptr; + void* InterfacePointer = nullptr; FORCEINLINE UObject*& GetObjectRef() { @@ -17,4 +17,5 @@ public: template class TScriptInterface : public FScriptInterface { +public: }; \ No newline at end of file diff --git a/Project Reboot 3.0/Stack.h b/Project Reboot 3.0/Stack.h index 71e4300..9992e63 100644 --- a/Project Reboot 3.0/Stack.h +++ b/Project Reboot 3.0/Stack.h @@ -49,7 +49,7 @@ public: // (GNatives[B])(Context, *this, RESULT_PARAM); } - __forceinline void StepCompiledIn(void* const Result/*, const FFieldClass* ExpectedPropertyType*/) // https://github.com/EpicGames/UnrealEngine/blob/cdaec5b33ea5d332e51eee4e4866495c90442122/Engine/Source/Runtime/CoreUObject/Public/UObject/Stack.h#L444 + __forceinline void StepCompiledIn(void* const Result/*, const FFieldClass* ExpectedPropertyType*/, bool bPrint = false) // https://github.com/EpicGames/UnrealEngine/blob/cdaec5b33ea5d332e51eee4e4866495c90442122/Engine/Source/Runtime/CoreUObject/Public/UObject/Stack.h#L444 { if (Code) { @@ -64,6 +64,9 @@ public: PropertyChainForCompiledIn = Property->Next; StepExplicitProperty(Result, Property); */ + if (bPrint) + LOG_INFO(LogDev, "No code!"); + void* Property = GetPropertyChainForCompiledIn(); GetPropertyChainForCompiledIn() = Engine_Version >= 425 ? *(void**)(__int64(Property) + 0x20) : ((UField*)Property)->Next; StepExplicitProperty(Result, Property); diff --git a/Project Reboot 3.0/TimerManager.h b/Project Reboot 3.0/TimerManager.h new file mode 100644 index 0000000..aabe2b8 --- /dev/null +++ b/Project Reboot 3.0/TimerManager.h @@ -0,0 +1,32 @@ +#pragma once + +#include "EngineTypes.h" +#include "Function.h" +#include "DelegateCombinations.h" + +DECLARE_DELEGATE(FTimerDelegate); + +struct FTimerUnifiedDelegate +{ + /** Holds the delegate to call. */ + FTimerDelegate FuncDelegate; + /** Holds the dynamic delegate to call. */ + FTimerDynamicDelegate FuncDynDelegate; + /** Holds the TFunction callback to call. */ + TFunction FuncCallback; + + FTimerUnifiedDelegate() {}; + FTimerUnifiedDelegate(FTimerDelegate const& D) : FuncDelegate(D) {}; +}; + +class FTimerManager // : public FNoncopyable +{ +public: + FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, FTimerDelegate const& InDelegate, float InRate, bool InbLoop, float InFirstDelay = -1.f) + { + static void (*InternalSetTimerOriginal)(__int64 TimerManager, FTimerHandle& InOutHandle, FTimerUnifiedDelegate&& InDelegate, float InRate, bool InbLoop, float InFirstDelay) = + decltype(InternalSetTimerOriginal)(Addresses::SetTimer); + + InternalSetTimerOriginal(__int64(this), InOutHandle, FTimerUnifiedDelegate(InDelegate), InRate, InbLoop, InFirstDelay); + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/TypeWrapper.h b/Project Reboot 3.0/TypeWrapper.h new file mode 100644 index 0000000..d9029f3 --- /dev/null +++ b/Project Reboot 3.0/TypeWrapper.h @@ -0,0 +1,16 @@ +#pragma once + +template +struct TTypeWrapper; + +template +struct TUnwrapType +{ + typedef T Type; +}; + +template +struct TUnwrapType> +{ + typedef T Type; +}; \ No newline at end of file diff --git a/Project Reboot 3.0/World.h b/Project Reboot 3.0/World.h index 4b44c82..9e8f9cc 100644 --- a/Project Reboot 3.0/World.h +++ b/Project Reboot 3.0/World.h @@ -5,6 +5,7 @@ #include "Object.h" #include "Rotator.h" #include "Actor.h" +#include "GameInstance.h" struct FNetworkNotify { @@ -46,6 +47,24 @@ public: return this->Get(AuthorityGameModeOffset); } + class AGameState* GetGameState() + { + static auto GameStateOffset = GetOffset("GameState"); + return this->Get(GameStateOffset); + } + + UGameInstance* GetOwningGameInstance() + { + static auto OwningGameInstanceOffset = GetOffset("OwningGameInstance"); + return this->Get(OwningGameInstanceOffset); + } + + inline FTimerManager& GetTimerManager() + { + return GetOwningGameInstance()->GetTimerManager(); + // return (GetOwningGameInstance() ? GetOwningGameInstance()->GetTimerManager() : *TimerManager); + } + template ActorType* SpawnActor(UClass* Class, FTransform UserTransformPtr = FTransform(), const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters()) { diff --git a/Project Reboot 3.0/addresses.cpp b/Project Reboot 3.0/addresses.cpp index 5a5f457..bdaae5b 100644 --- a/Project Reboot 3.0/addresses.cpp +++ b/Project Reboot 3.0/addresses.cpp @@ -263,6 +263,9 @@ void Addresses::FindAll() LOG_INFO(LogDev, "Finding EnterAircraft"); Addresses::EnterAircraft = FindEnterAircraft(); + LOG_INFO(LogDev, "Finding SetTimer"); + Addresses::SetTimer = FindSetTimer(); + LOG_INFO(LogDev, "Finished finding!"); } @@ -323,6 +326,7 @@ void Addresses::Print() LOG_INFO(LogDev, "GetInterfaceAddress: 0x{:x}", GetInterfaceAddress - Base); LOG_INFO(LogDev, "ApplyCharacterCustomization: 0x{:x}", ApplyCharacterCustomization - Base); LOG_INFO(LogDev, "EnterAircraft: 0x{:x}", EnterAircraft - Base); + LOG_INFO(LogDev, "SetTimer: 0x{:x}", SetTimer - Base); } void Offsets::FindAll() diff --git a/Project Reboot 3.0/addresses.h b/Project Reboot 3.0/addresses.h index 79c4802..eab627f 100644 --- a/Project Reboot 3.0/addresses.h +++ b/Project Reboot 3.0/addresses.h @@ -61,6 +61,7 @@ namespace Addresses extern inline uint64 ApplyCharacterCustomization = 0; extern inline uint64 GetInterfaceAddress = 0; extern inline uint64 EnterAircraft = 0; + extern inline uint64 SetTimer = 0; void SetupVersion(); // Finds Engine Version void FindAll(); diff --git a/Project Reboot 3.0/commands.h b/Project Reboot 3.0/commands.h index fea9e36..46bfc26 100644 --- a/Project Reboot 3.0/commands.h +++ b/Project Reboot 3.0/commands.h @@ -3,6 +3,7 @@ #include "reboot.h" #include "FortPlayerControllerAthena.h" #include "KismetSystemLibrary.h" +#include "AthenaBarrierObjective.h" bool IsOperator(APlayerState* PlayerState, AFortPlayerController* PlayerController) { diff --git a/Project Reboot 3.0/dllmain.cpp b/Project Reboot 3.0/dllmain.cpp index 4570fcb..ca4c398 100644 --- a/Project Reboot 3.0/dllmain.cpp +++ b/Project Reboot 3.0/dllmain.cpp @@ -37,6 +37,7 @@ #include "vendingmachine.h" #include "FortOctopusVehicle.h" #include "FortVolumeManager.h" +#include "FortAthenaMutator_Barrier.h" #include "PlaysetLevelStreamComponent.h" @@ -464,6 +465,13 @@ DWORD WINAPI Main(LPVOID) AFortPlayerControllerAthena::ServerReadyToStartMatchHook, (PVOID*)&AFortPlayerControllerAthena::ServerReadyToStartMatchOriginal, false); Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerControllerZone.ServerRequestSeatChange"), AFortPlayerControllerAthena::ServerRequestSeatChangeHook, (PVOID*)&AFortPlayerControllerAthena::ServerRequestSeatChangeOriginal, false); + if (false) + { + Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerControllerGameplay.StartGhostMode"), // (Milxnor) TODO: This changes to a component in later seasons. + AFortPlayerControllerAthena::StartGhostModeHook, (PVOID*)&AFortPlayerControllerAthena::StartGhostModeOriginal, false, true); + Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerControllerGameplay.EndGhostMode"), + AFortPlayerControllerAthena::EndGhostModeHook, (PVOID*)&AFortPlayerControllerAthena::EndGhostModeOriginal, false); + } Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerControllerAthena.ServerGiveCreativeItem"), AFortPlayerControllerAthena::ServerGiveCreativeItemHook, nullptr, true); Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerControllerAthena.ServerPlaySquadQuickChatMessage"), @@ -471,11 +479,8 @@ DWORD WINAPI Main(LPVOID) Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerControllerAthena.ServerTeleportToPlaygroundLobbyIsland"), AFortPlayerControllerAthena::ServerTeleportToPlaygroundLobbyIslandHook, nullptr, false); - Hooking::MinHook::Hook(FortPlayerStateAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerStateAthena.ServerSetInAircraft"), - AFortPlayerStateAthena::ServerSetInAircraftHook, (PVOID*)&AFortPlayerStateAthena::ServerSetInAircraftOriginal, false, true); // We could use second method but eh - - Hooking::MinHook::Hook(FindObject("/Script/FortniteGame.Default__FortAthenaMutator_GiveItemsAtGamePhaseStep"), FindObject(L"/Script/FortniteGame.FortAthenaMutator_GiveItemsAtGamePhaseStep.OnGamePhaseStepChanged"), - AFortAthenaMutator_GiveItemsAtGamePhaseStep::OnGamePhaseStepChangedHook, (PVOID*)&AFortAthenaMutator_GiveItemsAtGamePhaseStep::OnGamePhaseStepChangedOriginal, false, true); + // Hooking::MinHook::Hook(FortPlayerStateAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerStateAthena.ServerSetInAircraft"), + // AFortPlayerStateAthena::ServerSetInAircraftHook, (PVOID*)&AFortPlayerStateAthena::ServerSetInAircraftOriginal, false, true); // We could use second method but eh if (FortOctopusVehicleDefault) { @@ -506,6 +511,13 @@ DWORD WINAPI Main(LPVOID) Hooking::MinHook::Hook(FortGameplayAbilityAthena_PeriodicItemGrantDefault, FindObject(L"/Script/FortniteGame.FortGameplayAbilityAthena_PeriodicItemGrant.StartItemAwardTimers"), UFortGameplayAbilityAthena_PeriodicItemGrant::StartItemAwardTimersHook, (PVOID*)&UFortGameplayAbilityAthena_PeriodicItemGrant::StartItemAwardTimersOriginal, false, true); + Hooking::MinHook::Hook(FindObject("/Script/FortniteGame.Default__FortAthenaMutator_Barrier"), FindObject(L"/Script/FortniteGame.FortAthenaMutator_Barrier.OnGamePhaseStepChanged"), + AFortAthenaMutator_Barrier::OnGamePhaseStepChangedHook, (PVOID*)&AFortAthenaMutator_Barrier::OnGamePhaseStepChangedOriginal, false, true); + Hooking::MinHook::Hook(FindObject("/Script/FortniteGame.Default__FortAthenaMutator_Disco"), FindObject(L"/Script/FortniteGame.FortAthenaMutator_Disco.OnGamePhaseStepChanged"), + AFortAthenaMutator_Disco::OnGamePhaseStepChangedHook, (PVOID*)&AFortAthenaMutator_Disco::OnGamePhaseStepChangedOriginal, false, true); + Hooking::MinHook::Hook(FindObject("/Script/FortniteGame.Default__FortAthenaMutator_GiveItemsAtGamePhaseStep"), FindObject(L"/Script/FortniteGame.FortAthenaMutator_GiveItemsAtGamePhaseStep.OnGamePhaseStepChanged"), + AFortAthenaMutator_GiveItemsAtGamePhaseStep::OnGamePhaseStepChangedHook, (PVOID*)&AFortAthenaMutator_GiveItemsAtGamePhaseStep::OnGamePhaseStepChangedOriginal, false, true); + Hooking::MinHook::Hook(FortKismetLibraryDefault, FindObject(L"/Script/FortniteGame.FortKismetLibrary.K2_GiveItemToPlayer"), UFortKismetLibrary::K2_GiveItemToPlayerHook, (PVOID*)&UFortKismetLibrary::K2_GiveItemToPlayerOriginal, false, true); Hooking::MinHook::Hook(FortKismetLibraryDefault, FindObject(L"/Script/FortniteGame.FortKismetLibrary.K2_GiveBuildingResource"), diff --git a/Project Reboot 3.0/finder.h b/Project Reboot 3.0/finder.h index 181f9ae..cbad427 100644 --- a/Project Reboot 3.0/finder.h +++ b/Project Reboot 3.0/finder.h @@ -659,6 +659,38 @@ static inline uint64 FindSetZoneToIndex() // actually StartNewSafeZonePhase return 0; } +static inline uint64 FindSetTimer() +{ + auto Addr = Memcury::Scanner::FindStringRef(L"STAT_SetTimer", false).Get(); + + if (!Addr) + return 0; + + for (int i = 0; i < 1000; i++) + { + /* if ((*(uint8_t*)(uint8_t*)(Addr - i) == 0x40 && *(uint8_t*)(uint8_t*)(Addr - i + 1) == 0x53) + || (*(uint8_t*)(uint8_t*)(Addr - i) == 0x40 && *(uint8_t*)(uint8_t*)(Addr - i + 1) == 0x55)) + { + return Addr - i; + } + + if (Fortnite_Version < 8) + { + if (*(uint8_t*)(uint8_t*)(Addr - i) == 0x48 && *(uint8_t*)(uint8_t*)(Addr - i + 1) == 0x89 && *(uint8_t*)(uint8_t*)(Addr - i + 2) == 0x5C) + { + return Addr - i; + } + } */ + + if (*(uint8_t*)(uint8_t*)(Addr - i) == 0x48 && *(uint8_t*)(uint8_t*)(Addr - i + 1) == 0x8B && *(uint8_t*)(uint8_t*)(Addr - i + 2) == 0xC4) + { + return Addr - i; + } + } + + return 0; +} + static inline uint64 FindEnterAircraft() { auto Addr = Memcury::Scanner::FindStringRef(L"EnterAircraft: [%s] is attempting to enter aircraft after having already exited.", true, 0, Engine_Version >= 500).Get(); @@ -671,7 +703,12 @@ static inline uint64 FindEnterAircraft() return Addr - i; } - if (*(uint8_t*)(uint8_t*)(Addr - i) == 0x48 && *(uint8_t*)(uint8_t*)(Addr - i + 1) == 0x89 && *(uint8_t*)(uint8_t*)(Addr - i + 2) == 0x5C && *(uint8_t*)(uint8_t*)(Addr - i + 2) == 0x24) + /* if (*(uint8_t*)(uint8_t*)(Addr - i) == 0x48 && *(uint8_t*)(uint8_t*)(Addr - i + 1) == 0x89 && *(uint8_t*)(uint8_t*)(Addr - i + 2) == 0x5C && *(uint8_t*)(uint8_t*)(Addr - i + 3) == 0x24) + { + return Addr - i; + } */ + + if (*(uint8_t*)(uint8_t*)(Addr - i) == 0x48 && *(uint8_t*)(uint8_t*)(Addr - i + 1) == 0x89 && *(uint8_t*)(uint8_t*)(Addr - i + 2) == 0x5C && *(uint8_t*)(uint8_t*)(Addr - i + 3) == 0x74) // 4.1 { return Addr - i; } @@ -744,8 +781,8 @@ static inline uint64 FindApplyGadgetData() static inline uint64 FindGetInterfaceAddress() { - if (Engine_Version <= 420) - return Memcury::Scanner::FindPattern("48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 33 FF 48 8B DA 48 8B F1 48").Get(); // 4.1 + if (Engine_Version <= 421) + return Memcury::Scanner::FindPattern("48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 33 FF 48 8B DA 48 8B F1 48").Get(); // 4.1 & 6.21 return Memcury::Scanner::FindPattern("48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 33 DB 48 8B FA 48 8B F1 48 85 D2 0F 84 ? ? ? ? 8B 82 ? ? ? ? C1 E8").Get(); } diff --git a/Project Reboot 3.0/gui.h b/Project Reboot 3.0/gui.h index a9da4ca..abd2c71 100644 --- a/Project Reboot 3.0/gui.h +++ b/Project Reboot 3.0/gui.h @@ -22,6 +22,7 @@ #include #include +#include "FortAthenaMutator_Disco.h" #include "globals.h" #include "Fonts/ruda-bold.h" #include "Vector.h" @@ -36,6 +37,7 @@ #include "FortGadgetItemDefinition.h" #include "FortWeaponItemDefinition.h" #include "events.h" +#include "FortAthenaMutator_Heist.h" #define GAME_TAB 1 #define PLAYERS_TAB 2 @@ -45,8 +47,9 @@ #define LATEGAME_TAB 6 #define DUMP_TAB 7 #define UNBAN_TAB 8 -#define SETTINGS_TAB 9 -#define CREDITS_TAB 10 +#define DEVELOPER_TAB 9 +#define SETTINGS_TAB 10 +#define CREDITS_TAB 11 #define MAIN_PLAYERTAB 1 #define INVENTORY_PLAYERTAB 2 @@ -216,6 +219,10 @@ static int playerTabTab = MAIN_PLAYERTAB; void StaticUI() { +#ifndef PROD + ImGui::Checkbox("Log ProcessEvent", &Globals::bLogProcessEvent); +#endif + ImGui::Checkbox("Infinite Ammo", &Globals::bInfiniteAmmo); ImGui::Checkbox("Infinite Materials", &Globals::bInfiniteMaterials); @@ -306,6 +313,16 @@ void MainTabs() // maybe a Replication Stats for >3.3? +#ifndef PROD + if (ImGui::BeginTabItem("Developer")) + { + Tab = DEVELOPER_TAB; + PlayerTab = -1; + bInformationTab = false; + ImGui::EndTabItem(); + } +#endif + if (false && ImGui::BeginTabItem(("Credits"))) { Tab = CREDITS_TAB; @@ -365,9 +382,7 @@ void MainUI() if (bLoaded) { StaticUI(); -#ifndef PROD - ImGui::Checkbox("Log ProcessEvent", &Globals::bLogProcessEvent); -#endif + if (!bStartedBus) { bool bWillBeLategame = Globals::bLateGame.load(); @@ -441,6 +456,44 @@ void MainUI() } } + if (ImGui::Button("TEST")) + { + auto GameMode = (AFortGameMode*)GetWorld()->GetGameMode(); + auto GameState = GameMode->GetGameState(); + + static auto mutatorClass = FindObject("/Script/FortniteGame.FortAthenaMutator"); + auto AllMutators = UGameplayStatics::GetAllActorsOfClass(GetWorld(), mutatorClass); + + for (int i = 0; i < AllMutators.Num(); i++) + { + auto Mutator = AllMutators.at(i); + + LOG_INFO(LogDev, "[{}] Mutator: {}", i, Mutator->GetFullName()); + + if (auto DiscoMutator = Cast(Mutator)) + { + auto& ControlPointSpawnData = DiscoMutator->GetControlPointSpawnData(); + + LOG_INFO(LogDev, "ControlPointSpawnData.Num(): {}", ControlPointSpawnData.Num()); + } + else if (auto HeistMutator = Cast(Mutator)) + { + auto& HeistExitCraftSpawnData = HeistMutator->GetHeistExitCraftSpawnData(); + + LOG_INFO(LogDev, "HeistExitCraftSpawnData.Num(): {}", HeistExitCraftSpawnData.Num()); + + for (int j = 0; j < HeistExitCraftSpawnData.Num(); j++) + { + auto& CurrentHeistExitCraftSpawnData = HeistExitCraftSpawnData.at(j); + auto CurveTable = CurrentHeistExitCraftSpawnData.SpawnDelayTime.GetCurve().CurveTable; + + // LOG_INFO(LogDev, "{} {}", CurveTable ? CurveTable->GetFullName() : "InvalidTable", + // CurrentHeistExitCraftSpawnData.SpawnDelayTime.GetCurve().RowName.IsValid() ? CurrentHeistExitCraftSpawnData.SpawnDelayTime.GetCurve().RowName.ToString() : "InvalidName"); + } + } + } + } + if (!bStartedBus) { if (Globals::bLateGame.load()) @@ -743,6 +796,22 @@ void MainUI() else if (Tab == UNBAN_TAB) { + } + else if (Tab == DEVELOPER_TAB) + { + static std::string ClassNameToDump; + + ImGui::InputText("Class Name to get VFT", &ClassNameToDump); + + if (ImGui::Button("Print Class VFT")) + { + auto ClassToDump = FindObject(ClassNameToDump)->CreateDefaultObject(); + + if (ClassToDump) + { + LOG_INFO(LogDev, "{} VFT: 0x{:x}", ClassToDump->GetName(), __int64(ClassToDump->VFTable) - __int64(GetModuleHandleW(0))); + } + } } else if (Tab == SETTINGS_TAB) { diff --git a/Project Reboot 3.0/reboot.h b/Project Reboot 3.0/reboot.h index be7514d..43efa10 100644 --- a/Project Reboot 3.0/reboot.h +++ b/Project Reboot 3.0/reboot.h @@ -377,7 +377,7 @@ static void CopyStruct(void* Dest, void* Src, size_t Size, UStruct* Struct = nul } template -static T* Alloc(size_t Size, bool bUseRealloc = false) +static T* Alloc(size_t Size = sizeof(T), bool bUseRealloc = false) { return bUseRealloc ? (T*)FMemory::Realloc(0, Size, 0) : (T*)VirtualAlloc(0, Size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); }