Files
Project-Reboot-3.0/Project Reboot 3.0/NetDriver.cpp
Milxnor 406090ddcd a bit
fix pickup notifications, fix 1.11 crash, remove pickup notifications (to fix restarting and random crashes), fix s7 crash, add delay before switching level, fix some issues with gadgets, fix character parts being buggy.
2023-04-16 00:28:59 -04:00

1060 lines
36 KiB
C++

#include "NetDriver.h"
#include "reboot.h"
#include "Actor.h"
#include "NetConnection.h"
#include "FortPlayerControllerAthena.h"
#include "GameplayStatics.h"
#include "KismetMathLibrary.h"
#include <random>
#include "GenericPlatformMath.h"
#include "ActorChannel.h"
#include "KismetSystemLibrary.h"
#include "UnrealMathUtility.h"
#include "FortQuickBars.h"
#include "Sort.h"
#include "Sorting.h"
void UNetDriver::TickFlushHook(UNetDriver* NetDriver)
{
static auto ReplicationDriverOffset = NetDriver->GetOffset("ReplicationDriver", false);
if (ReplicationDriverOffset == -1)
{
NetDriver->ServerReplicateActors();
}
else
{
if (auto ReplicationDriver = NetDriver->Get(ReplicationDriverOffset))
reinterpret_cast<void(*)(UObject*)>(ReplicationDriver->VFTable[Offsets::ServerReplicateActors])(ReplicationDriver);
}
return TickFlushOriginal(NetDriver);
}
void Test3(AActor* Actor, const std::string& Str)
{
if (Actor->IsA(AFortPawn::StaticClass()))
{
LOG_INFO(LogDev, "[{}] Pawn", Str)
}
if (Actor->IsA(AFortQuickBars::StaticClass()))
{
LOG_INFO(LogDev, "[{}] QuickBars", Str)
}
}
#define NAME_None 0
static FNetViewer ConstructNetViewer(UNetConnection* NetConnection)
{
FNetViewer newViewer{};
newViewer.Connection = NetConnection;
newViewer.InViewer = NetConnection->GetPlayerController() ? NetConnection->GetPlayerController() : NetConnection->GetOwningActor();
newViewer.ViewTarget = NetConnection->GetViewTarget();
// if (!NetConnection->GetOwningActor() || !(!NetConnection->GetPlayerController() || (NetConnection->GetPlayerController() == NetConnection->GetOwningActor())))
// return newViewer;
APlayerController* ViewingController = NetConnection->GetPlayerController();
newViewer.ViewLocation = newViewer.ViewTarget->GetActorLocation();
if (ViewingController)
{
static auto ControlRotationOffset = ViewingController->GetOffset("ControlRotation");
FRotator ViewRotation = ViewingController->Get<FRotator>(ControlRotationOffset); // hmmmm // ViewingController->GetControlRotation();
// AFortPlayerControllerAthena::GetPlayerViewPointHook(Cast<AFortPlayerControllerAthena>(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<FNetworkObjectList>*)(__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 <typename T = __int64>
T* AllocForReplication(size_t Size)
{
return (T*)FMemory::Realloc(nullptr, Size, 0);
return Alloc<T>(Size);
}
void SetChannelActorForDestroy(UActorChannel* ActorChannel, FActorDestructionInfo* DestructInfo)
{
static auto ConnectionOffset = ActorChannel->GetOffset("Connection");
UNetConnection* Connection = ActorChannel->Get<UNetConnection*>(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<FNetViewer>& 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<int32>(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<FNetViewer>& 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<int32>(Priority, 65536.0f * Time);
}
}
TSet<FNetworkGUID>& GetConnectionDestroyedStartupOrDormantActors(UNetConnection* Connection)
{
return *(TSet<FNetworkGUID>*)(__int64(Connection) + 0x33678);
}
TMap<FNetworkGUID, FActorDestructionInfo>& GetDriverDestroyedStartupOrDormantActors(UNetDriver* NetDriver)
{
return *(TMap<FNetworkGUID, FActorDestructionInfo>*)(__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++)
{
UNetConnection* Connection = ClientConnections.at(ConnIdx);
if (!Connection) continue;
// check(Connection->State == USOCK_Pending || Connection->State == USOCK_Open || Connection->State == USOCK_Closed);
// checkSlow(Connection->GetUChildConnection() == NULL);
AActor* OwningActor = Connection->GetOwningActor();
if (OwningActor != NULL && /* Connection->State == USOCK_Open && */ (Connection->GetDriver()->GetTime() - Connection->GetLastReceiveTime() < 1.5f))
{
// check( World == OwningActor->GetWorld() );
bFoundReadyConnection = true;
AActor* DesiredViewTarget = OwningActor;
if (Connection->GetPlayerController())
{
if (AActor* ViewTarget = Connection->GetPlayerController()->GetViewTarget())
{
DesiredViewTarget = ViewTarget;
}
}
Connection->GetViewTarget() = DesiredViewTarget;
}
else
{
Connection->GetViewTarget() = NULL;
}
}
return bFoundReadyConnection ? NumClientsToTick : 0;
}
enum class ENetRole : uint8_t
{
ROLE_None = 0,
ROLE_SimulatedProxy = 1,
ROLE_AutonomousProxy = 2,
ROLE_Authority = 3,
ROLE_MAX = 4
};
FORCEINLINE float FRand()
{
return ReplicationRandStream.FRand();
}
#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<std::chrono::milliseconds>(now);
auto value = now_ms.time_since_epoch().count();
std::default_random_engine generator(value);
std::uniform_real_distribution<float> distribution(0.0f, 1.0f);
return distribution(generator);
}
#endif
void UNetDriver::ServerReplicateActors_BuildConsiderList(std::vector<FNetworkObjectInfo*>& OutConsiderList, const float ServerTickTime)
{
std::vector<AActor*> ActorsToRemove;
auto World = GetWorld();
for (const TSharedPtr<FNetworkObjectInfo>& 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<ENetRole>(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<FNetViewer>& 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<bool(*)(AActor*, AActor*, AActor*, const FVector&)>(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<UNetConnection>& 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<FNetViewer>& 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<FNetViewer>& ConnectionViewers, UActorChannel* Channel, const float Time, const bool bLowNetBandwidth)
{
using enum ENetDormancy;
static auto bPendingDormancyOffset = 0x30;
static auto bPendingDormancyFieldMask = 0x0;
static auto DormantOffset = 0x30;
static auto DormantFieldMask = 0x0;
if (Actor->GetNetDormancy() <= DORM_Awake || !Channel
// || ReadBitfield((PlaceholderBitfield*)(__int64(Channel) + bPendingDormancyOffset), bPendingDormancyFieldMask)
// || ReadBitfield((PlaceholderBitfield*)(__int64(Channel) + DormantOffset), DormantFieldMask)
|| Channel->IsPendingDormancy()
|| Channel->IsDormant()
)
{
// Either shouldn't go dormant, or is already dormant
return false;
}
if (Actor->GetNetDormancy() == DORM_DormantPartial)
{
for (int32 viewerIdx = 0; viewerIdx < ConnectionViewers.size(); viewerIdx++)
{
// if (!Actor->GetNetDormancy(ConnectionViewers[viewerIdx].ViewLocation, ConnectionViewers[viewerIdx].ViewDir, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, Channel, Time, bLowNetBandwidth))
if (!false) // ^ this just returns false soo (atleast AActor implementation)
{
return false;
}
}
}
return true;
}
UActorChannel* FindChannel(UNetConnection* Connection, FNetworkObjectInfo* ActorInfo)
{
if (true)
{
auto& ActorChannels = Connection->GetActorChannels();
for (auto& Pair : ActorChannels)
{
if (Pair.First == ActorInfo->WeakActor)
{
return Pair.Second;
}
}
return nullptr;
}
auto Actor = ActorInfo->Actor;
if (!Actor)
return nullptr;
static auto OpenChannelsOffset = Connection->GetOffset("OpenChannels");
auto& OpenChannels = Connection->Get<TArray<UChannel*>>(OpenChannelsOffset);
static auto ActorChannelClass = FindObject<UClass>("/Script/Engine.ActorChannel");
// LOG_INFO(LogReplication, "OpenChannels.Num(): {}", OpenChannels.Num());
for (int i = 0; i < OpenChannels.Num(); i++)
{
auto Channel = OpenChannels.at(i);
if (!Channel)
continue;
// LOG_INFO(LogReplication, "[{}] Class {}", i, Channel->ClassPrivate ? Channel->ClassPrivate->GetFullName() : "InvalidObject");
// if (!Channel->IsA(ActorChannelClass))
// continue;
if (Channel->ClassPrivate != ActorChannelClass)
continue;
static auto ActorOffset = Channel->GetOffset("Actor");
if (Channel->Get<AActor*>(ActorOffset) != Actor)
continue;
return (UActorChannel*)Channel;
}
return NULL;
}
int32 UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection* Connection, const std::vector<FNetViewer>& ConnectionViewers, const std::vector<FNetworkObjectInfo*> ConsiderList, const bool bCPUSaturated, FActorPriority*& OutPriorityList, FActorPriority**& OutPriorityActors)
{
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();
}
// check( World == Connection->OwningActor->GetWorld() );
int32 FinalSortedCount = 0;
int32 DeletedCount = 0;
// Make weak ptr once for IsActorDormant call
TWeakObjectPtr<UNetConnection> 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<FActorPriority>(MaxSortedActors * sizeof(FActorPriority));
// (FActorPriority*)FMemory::Realloc(nullptr, MaxSortedActors * sizeof(FActorPriority), 0);
OutPriorityActors = Alloc<FActorPriority*>(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++)
{
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");
}
}
// 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;
}
int32 UNetDriver::ServerReplicateActors_ProcessPrioritizedActors(UNetConnection* Connection, const std::vector<FNetViewer>& ConnectionViewers, FActorPriority** PriorityActors, const int32 FinalSortedCount, int32& OutUpdated)
{
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);
int32 ActorUpdatesThisConnection = 0;
int32 ActorUpdatesThisConnectionSent = 0;
int32 FinalRelevantCount = 0;
if (!Connection->IsNetReady(0))
{
return 0;
}
for (int32 j = 0; j < FinalSortedCount; j++)
{
FNetworkObjectInfo* ActorInfo = PriorityActors[j]->ActorInfo;
// Deletion entry
if (ActorInfo == NULL && PriorityActors[j]->DestructionInfo)
{
// 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;
}
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;
}
int32 UNetDriver::ServerReplicateActors()
{
int32 Updated = 0;
++(*(int*)(__int64(this) + Offsets::ReplicationFrame));
const int32 NumClientsToTick = ServerReplicateActors_PrepConnections(this);
if (NumClientsToTick == 0)
{
// No connections are ready this frame
return 0;
}
auto World = GetNetDriverWorld();
AWorldSettings* WorldSettings = World->GetWorldSettings();
// bool bCPUSaturated = false;
float ServerTickTime = GetMaxTickRateHook();
if (ServerTickTime == 0.f)
{
// ServerTickTime = DeltaSeconds;
}
else
{
ServerTickTime = 1.f / ServerTickTime;
// bCPUSaturated = DeltaSeconds > 1.2f * ServerTickTime;
}
std::vector<FNetworkObjectInfo*> ConsiderList;
ConsiderList.reserve(GetNetworkObjectList().ActiveNetworkObjects.Num());
// std::cout << "ConsiderList.size(): " << GetNetworkObjectList(NetDriver).ActiveNetworkObjects.Num() << '\n';
ServerReplicateActors_BuildConsiderList(ConsiderList, ServerTickTime);
bool bCPUSaturated = false;
auto& ClientConnections = GetClientConnections();
for (int32 i = 0; i < ClientConnections.Num(); i++)
{
UNetConnection* Connection = ClientConnections.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 (Replicator.OwningChannel != nullptr)
{
Replicator.ValidateAgainstState(Replicator.OwningChannel->GetActor());
}
}
} */
// if this client shouldn't be ticked this frame
if (i >= NumClientsToTick)
{
//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++)
{
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
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<bool>(TimeSensitiveOffset) = false;
}
else if (Connection->GetViewTarget())
{
// Make a list of viewers this connection should consider (this connection and children of this connection)
// TArray<FNetViewer>& ConnectionViewers = WorldSettings->ReplicationViewers;
// ConnectionViewers.Reset();
std::vector<FNetViewer> 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())
{
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)
{
// A deletion entry, skip it because we dont have anywhere to store a 'better give higher priority next time'
continue;
}
AActor* Actor = PriorityActors[k]->ActorInfo->Actor;
UActorChannel* Channel = PriorityActors[k]->Channel;
if (Channel != NULL && GetTime() - Channel->GetRelevantTime() <= 1.f)
{
PriorityActors[k]->ActorInfo->bPendingNetUpdate = true;
}
else if (IsActorRelevantToConnection(Actor, ConnectionViewers))
{
PriorityActors[k]->ActorInfo->bPendingNetUpdate = true;
if (Channel != NULL)
{
Channel->GetRelevantTime() = GetTime() + 0.5f * SRand();
}
}
}
ConnectionViewers.clear();
}
}
// shuffle the list of connections if not all connections were ticked
/* if (NumClientsToTick < ClientConnections.Num())
{
int32 NumConnectionsToMove = NumClientsToTick;
while (NumConnectionsToMove > 0)
{
// move all the ticked connections to the end of the list so that the other connections are considered first for the next frame
UNetConnection* Connection = ClientConnections.at(0);
ClientConnections.RemoveAt(0, 1);
ClientConnections.Add(Connection);
NumConnectionsToMove--;
}
} */
return Updated;
}