#include "NetDriver.h" #include "reboot.h" #include "Actor.h" #include "NetConnection.h" #include "FortPlayerControllerAthena.h" #include "GameplayStatics.h" #include "KismetMathLibrary.h" #include #include "Package.h"S #include "AssertionMacros.h" #include "bots.h" #include "gui.h" FNetworkObjectList& UNetDriver::GetNetworkObjectList() { return *(*(TSharedPtr*)(__int64(this) + Offsets::NetworkObjectList)); } bool ShouldUseNetworkObjectList() { return Fortnite_Version < 20; } void UNetDriver::RemoveNetworkActor(AActor* Actor) { GetNetworkObjectList().Remove(Actor); // RenamedStartupActors.Remove(Actor->GetFName()); } void UNetDriver::TickFlushHook(UNetDriver* NetDriver) { if (bShouldDestroyAllPlayerBuilds) // i hate this { auto AllBuildingSMActors = UGameplayStatics::GetAllActorsOfClass(GetWorld(), ABuildingSMActor::StaticClass()); for (int i = 0; i < AllBuildingSMActors.Num(); i++) { auto CurrentBuildingSMActor = (ABuildingSMActor*)AllBuildingSMActors.at(i); if (CurrentBuildingSMActor->IsDestroyed() || CurrentBuildingSMActor->IsActorBeingDestroyed() || !CurrentBuildingSMActor->IsPlayerPlaced()) continue; CurrentBuildingSMActor->SilentDie(); // CurrentBuildingSMActor->K2_DestroyActor(); } AllBuildingSMActors.Free(); bShouldDestroyAllPlayerBuilds = false; } /* if (bEnableBotTick) { Bots::Tick(); } */ if (Globals::bStartedListening) { static auto ReplicationDriverOffset = NetDriver->GetOffset("ReplicationDriver"/*, false */); // LOG_INFO(LogDev, "ReplicationDriverOffset{}", ReplicationDriverOffset); // if (ReplicationDriverOffset == -1) if (ReplicationDriverOffset == -1 || Fortnite_Version >= 20) { NetDriver->ServerReplicateActors(); } else { if (auto ReplicationDriver = NetDriver->Get(ReplicationDriverOffset)) { reinterpret_cast(ReplicationDriver->VFTable[Offsets::ServerReplicateActors])(ReplicationDriver); } else { // LOG_INFO(LogDev, "ReplicationDriver is nul!!?1//33/221/4/124/123"); // 3.3 MOMENT } } } return TickFlushOriginal(NetDriver); } enum class EChannelCreateFlags : uint32_t { None = (1 << 0), OpenedLocally = (1 << 1) }; int32 ServerReplicateActors_PrepConnections(UNetDriver* NetDriver) { auto& ClientConnections = NetDriver->GetClientConnections(); int32 NumClientsToTick = ClientConnections.Num(); 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->Driver->Time - Connection->LastReceiveTime < 1.5f)) { 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() { std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<> dis(0, 1); float random_number = dis(gen); return random_number; } static FORCEINLINE bool ShouldActorGoDormant(AActor* Actor, const std::vector& ConnectionViewers, UActorChannel* Channel, const float Time, const bool bLowNetBandwidth) { using enum ENetDormancy; static auto bPendingDormancyOffset = 0x30; static auto bPendingDormancyFieldMask = 0x0; static auto DormantOffset = 0x30; static auto DormantFieldMask = 0x0; if (Actor->GetNetDormancy() <= DORM_Awake || !Channel // || ReadBitfield((PlaceholderBitfield*)(__int64(Channel) + bPendingDormancyOffset), bPendingDormancyFieldMask) // || ReadBitfield((PlaceholderBitfield*)(__int64(Channel) + DormantOffset), DormantFieldMask) || Channel->IsPendingDormancy() || Channel->IsDormant() ) { // Either shouldn't go dormant, or is already dormant return false; } if (Actor->GetNetDormancy() == DORM_DormantPartial) { for (int32 viewerIdx = 0; viewerIdx < ConnectionViewers.size(); viewerIdx++) { // if (!Actor->GetNetDormancy(ConnectionViewers[viewerIdx].ViewLocation, ConnectionViewers[viewerIdx].ViewDir, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, Channel, Time, bLowNetBandwidth)) if (!false) // ^ this just returns false soo (atleast AActor implementation) { return false; } } } return true; } void UNetDriver::ServerReplicateActors_BuildConsiderList(std::vector& OutConsiderList, float ServerTickTime) { std::vector ActorsToRemove; if (ShouldUseNetworkObjectList()) { auto& ActiveObjects = GetNetworkObjectList().ActiveNetworkObjects; auto World = GetWorld(); for (const TSharedPtr& ActorInfo : ActiveObjects) { if (!ActorInfo->bPendingNetUpdate && UGameplayStatics::GetTimeSeconds(GetWorld()) <= ActorInfo->NextUpdateTime) { continue; } auto Actor = ActorInfo->Actor; if (!Actor) continue; if (Actor->IsPendingKillPending()) // if (Actor->IsPendingKill()) { ActorsToRemove.push_back(Actor); continue; } static auto RemoteRoleOffset = Actor->GetOffset("RemoteRole"); if (Actor->Get(RemoteRoleOffset) == ENetRole::ROLE_None) { ActorsToRemove.push_back(Actor); continue; } // We should add a NetDriverName check but I don't believe it is needed. // We should check if the actor is initialized here. // We should check the level stuff here. static auto NetDormancyOffset = Actor->GetOffset("NetDormancy"); if (Actor->Get(NetDormancyOffset) == ENetDormancy::DORM_Initial && Actor->IsNetStartupActor()) // IsDormInitialStartupActor { continue; } // We should check NeedsLoadForClient here. // We should make sure the actor is in the same world here but I don't believe it is needed. if (ActorInfo->LastNetReplicateTime == 0) { ActorInfo->LastNetReplicateTime = UGameplayStatics::GetTimeSeconds(World); ActorInfo->OptimalNetUpdateDelta = 1.0f / Actor->GetNetUpdateFrequency(); } const float ScaleDownStartTime = 2.0f; const float ScaleDownTimeRange = 5.0f; const float LastReplicateDelta = UGameplayStatics::GetTimeSeconds(World) - ActorInfo->LastNetReplicateTime; if (LastReplicateDelta > ScaleDownStartTime) { static auto MinNetUpdateFrequencyOffset = Actor->GetOffset("MinNetUpdateFrequency"); if (Actor->Get(MinNetUpdateFrequencyOffset) == 0.0f) { Actor->Get(MinNetUpdateFrequencyOffset) = 2.0f; } const float MinOptimalDelta = 1.0f / Actor->GetNetUpdateFrequency(); // Don't go faster than NetUpdateFrequency const float MaxOptimalDelta = max(1.0f / Actor->GetMinNetUpdateFrequency(), MinOptimalDelta); // Don't go slower than MinNetUpdateFrequency (or NetUpdateFrequency if it's slower) const float Alpha = std::clamp((LastReplicateDelta - ScaleDownStartTime) / ScaleDownTimeRange, 0.0f, 1.0f); // should we use fmath? ActorInfo->OptimalNetUpdateDelta = std::lerp(MinOptimalDelta, MaxOptimalDelta, Alpha); // should we use fmath? } if (!ActorInfo->bPendingNetUpdate) { constexpr bool bUseAdapativeNetFrequency = false; const float NextUpdateDelta = bUseAdapativeNetFrequency ? ActorInfo->OptimalNetUpdateDelta : 1.0f / Actor->GetNetUpdateFrequency(); // then set the next update time float ServerTickTime = 1.f / 30; ActorInfo->NextUpdateTime = UGameplayStatics::GetTimeSeconds(World) + FRand() * ServerTickTime + NextUpdateDelta; static auto TimeOffset = GetOffset("Time"); ActorInfo->LastNetUpdateTime = Get(TimeOffset); } ActorInfo->bPendingNetUpdate = false; OutConsiderList.push_back(ActorInfo.Get()); static void (*CallPreReplication)(AActor*, UNetDriver*) = decltype(CallPreReplication)(Addresses::CallPreReplication); CallPreReplication(Actor, this); } } else { auto Actors = UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass()); for (int i = 0; i < Actors.Num(); ++i) { auto Actor = Actors.at(i); if (Actor->IsPendingKillPending()) // if (Actor->IsPendingKill()) { ActorsToRemove.push_back(Actor); continue; } static auto RemoteRoleOffset = Actor->GetOffset("RemoteRole"); if (Actor->Get(RemoteRoleOffset) == ENetRole::ROLE_None) { ActorsToRemove.push_back(Actor); continue; } // We should add a NetDriverName check but I don't believe it is needed. // We should check if the actor is initialized here. // We should check the level stuff here. static auto NetDormancyOffset = Actor->GetOffset("NetDormancy"); if (Actor->Get(NetDormancyOffset) == ENetDormancy::DORM_Initial && Actor->IsNetStartupActor()) // IsDormInitialStartupActor { continue; } auto ActorInfo = new FNetworkObjectInfo; ActorInfo->Actor = Actor; OutConsiderList.push_back(ActorInfo); static void (*CallPreReplication)(AActor*, UNetDriver*) = decltype(CallPreReplication)(Addresses::CallPreReplication); CallPreReplication(Actor, this); } Actors.Free(); } for (auto Actor : ActorsToRemove) { if (!Actor) continue; /* LOG_INFO(LogDev, "Removing actor: {}", Actor ? Actor->GetFullName() : "InvalidObject"); RemoveNetworkActor(Actor); LOG_INFO(LogDev, "Finished removing actor."); */ } } static UActorChannel* FindChannel(AActor * Actor, UNetConnection * Connection) { static auto OpenChannelsOffset = Connection->GetOffset("OpenChannels"); auto& OpenChannels = Connection->Get>(OpenChannelsOffset); static auto ActorChannelClass = FindObject(L"/Script/Engine.ActorChannel"); // LOG_INFO(LogReplication, "OpenChannels.Num(): {}", OpenChannels.Num()); for (int i = 0; i < OpenChannels.Num(); ++i) { auto Channel = OpenChannels.at(i); if (!Channel) continue; // LOG_INFO(LogReplication, "[{}] Class {}", i, Channel->ClassPrivate ? Channel->ClassPrivate->GetFullName() : "InvalidObject"); if (!Channel->IsA(ActorChannelClass)) // (Channel->ClassPrivate == ActorChannelClass) continue; static auto ActorOffset = Channel->GetOffset("Actor"); auto ChannelActor = Channel->Get(ActorOffset); // LOG_INFO(LogReplication, "[{}] {}", i, ChannelActor->GetFullName()); if (ChannelActor != Actor) continue; return (UActorChannel*)Channel; } // LOG_INFO(LogDev, "Failed to find channel for {}!", Actor->GetName()); return nullptr; } static bool IsActorRelevantToConnection(AActor * Actor, std::vector&ConnectionViewers) { for (int32 viewerIdx = 0; viewerIdx < ConnectionViewers.size(); viewerIdx++) { if (!ConnectionViewers[viewerIdx].ViewTarget) continue; // static bool (*IsNetRelevantFor)(AActor*, AActor*, AActor*, FVector&) = decltype(IsNetRelevantFor)(__int64(GetModuleHandleW(0)) + 0x1ECC700); static auto index = Offsets::IsNetRelevantFor; // if (Actor->IsNetRelevantFor(ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) // if (IsNetRelevantFor(Actor, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) if (reinterpret_cast(Actor->VFTable[index])( Actor, ConnectionViewers[viewerIdx].InViewer, ConnectionViewers[viewerIdx].ViewTarget, ConnectionViewers[viewerIdx].ViewLocation)) { return true; } } return false; } static FNetViewer ConstructNetViewer(UNetConnection* NetConnection) { FNetViewer newViewer{}; newViewer.Connection = NetConnection; newViewer.InViewer = NetConnection->GetPlayerController() ? NetConnection->GetPlayerController() : NetConnection->GetOwningActor(); newViewer.ViewTarget = NetConnection->GetViewTarget(); if (!NetConnection->GetOwningActor() || !(!NetConnection->GetPlayerController() || (NetConnection->GetPlayerController() == NetConnection->GetOwningActor()))) return newViewer; APlayerController* ViewingController = NetConnection->GetPlayerController(); newViewer.ViewLocation = newViewer.ViewTarget->GetActorLocation(); if (ViewingController) { static auto ControlRotationOffset = ViewingController->GetOffset("ControlRotation"); FRotator ViewRotation = ViewingController->Get(ControlRotationOffset); // hmmmm // ViewingController->GetControlRotation(); // AFortPlayerControllerAthena::GetPlayerViewPointHook(Cast(ViewingController, false), newViewer.ViewLocation, ViewRotation); ViewingController->GetActorEyesViewPoint(&newViewer.ViewLocation, &ViewRotation); // HMMM // static auto GetActorEyesViewPointOffset = 0x5B0; // void (*GetActorEyesViewPointOriginal)(AController*, FVector * a2, FRotator * a3) = decltype(GetActorEyesViewPointOriginal)(ViewingController->VFTable[GetActorEyesViewPointOffset / 8]); // GetActorEyesViewPointOriginal(ViewingController, &newViewer.ViewLocation, &ViewRotation); // AFortPlayerControllerAthena::GetPlayerViewPointHook((AFortPlayerControllerAthena*)ViewingController, newViewer.ViewLocation, ViewRotation); newViewer.ViewDir = ViewRotation.Vector(); } return newViewer; } static FORCEINLINE bool IsActorDormant(FNetworkObjectInfo* ActorInfo, const TWeakObjectPtr& Connection) { // If actor is already dormant on this channel, then skip replication entirely return ActorInfo->DormantConnections.Contains(Connection); } bool UNetDriver::IsLevelInitializedForActor(const AActor* InActor, const UNetConnection* InConnection) const { if (Fortnite_Version >= 2.42) // idk { return true; } /* #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // (Milxnor) This is on some ue versions and others not. if (!InActor || !InConnection) return false; // check(World == InActor->GetWorld()); #endif */ bool bFirstWorldCheck = Engine_Version == 416 ? (InConnection->GetClientWorldPackageName() == GetWorld()->GetOutermost()->GetFName()) : (InConnection->GetClientWorldPackageName() == GetWorldPackage()->NamePrivate); const bool bCorrectWorld = (bFirstWorldCheck && InConnection->ClientHasInitializedLevelFor(InActor)); const bool bIsConnectionPC = (InActor == InConnection->GetPlayerController()); return bCorrectWorld || bIsConnectionPC; } TMap* GetDestroyedStartupOrDormantActors(UNetDriver* Driver) { static int off = Fortnite_Version == 1.11 ? 0x228 : 0; // 0x240 return off == 0 ? nullptr : (TMap*)(__int64(Driver) + off); } TSet* GetDestroyedStartupOrDormantActors(UNetConnection* NetConnection) { static int off = Fortnite_Version == 1.11 ? 0x33678 : 0; return off == 0 ? nullptr : (TSet*)(__int64(NetConnection) + off); } using FArchive = void; bool IsError(FArchive* Ar) { return false; } void SerializeChecksum(FArchive* Ar, uint32 x, bool ErrorOK) { /* if (Ar->IsLoading()) { uint32 Magic = 0; Ar << Magic; if ((!ErrorOK || !IsError(Ar)) // && !ensure(Magic == x) ) { // UE_LOG(LogCoreNet, Warning, TEXT("%d == %d"), Magic, x); } } else { uint32 Magic = x; Ar << Magic; } */ } #define NET_CHECKSUM(Ser) \ { \ SerializeChecksum(Ser,0xE282FA84, false); \ } 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) const { return (First <= PacketId && PacketId <= Last); } }; void SetChannelActorForDestroy(UActorChannel* Channel, FActorDestructionInfo* DestructInfo) { auto Connection = Channel->GetConnection(); if ( true // && !Channel->IsClosing() // && (Connection->State == USOCK_Open || Connection->State == USOCK_Pending) ) { // Send a close notify, and wait for ack. struct FOutBunch { char pad[0x600]; // idk real size }; FOutBunch CloseBunch{}; FOutBunch(*ConstructorFOutBunch)(FOutBunch*, UChannel* , bool) = decltype(ConstructorFOutBunch)(__int64(GetModuleHandleW(0)) + 0x194E800); ConstructorFOutBunch(&CloseBunch, Channel, 1); // check(!CloseBunch.IsError()); // check(CloseBunch.bClose); // https://imgur.com/a/EtKFkrD *(bool*)(__int64(&CloseBunch) + 0xE8) = 1; *(bool*)(__int64(&CloseBunch) + 0xE6) = 0; // Serialize DestructInfo // NET_CHECKSUM(CloseBunch); // This is to mirror the Checksum in UPackageMapClient::SerializeNewActor using UPackageMap = UObject; reinterpret_cast(Connection->GetPackageMap()->VFTable[0x238 / 8])(Connection->GetPackageMap(), &CloseBunch, DestructInfo->ObjOuter.Get(), DestructInfo->NetGUID, DestructInfo->PathName); // UE_LOG(LogNetTraffic, Log, TEXT("SetChannelActorForDestroy: Channel %d. NetGUID <%s> Path: %s. Bits: %d"), ChIndex, *DestructInfo->NetGUID.ToString(), *DestructInfo->PathName, CloseBunch.GetNumBits()); // UE_LOG(LogNetDormancy, Verbose, TEXT("SetChannelActorForDestroy: Channel %d. NetGUID <%s> Path: %s. Bits: %d"), ChIndex, *DestructInfo->NetGUID.ToString(), *DestructInfo->PathName, CloseBunch.GetNumBits()); // 0x196E9C0 reinterpret_cast(Channel->VFTable[0x288 / 8])(Channel, &CloseBunch, false); } } TSet* GetClientVisibleLevelNames(UNetConnection* NetConnection) { return (TSet*)(__int64(NetConnection) + 0x336C8); } 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; } // AFortWorldSettings* WorldSettings = GetFortWorldSettings(NetDriver->World); // bool bCPUSaturated = false; float ServerTickTime = GetMaxTickRateHook(); /* if (ServerTickTime == 0.f) { ServerTickTime = DeltaSeconds; } else */ { ServerTickTime = 1.f / ServerTickTime; // bCPUSaturated = DeltaSeconds > 1.2f * ServerTickTime; } std::vector ConsiderList; if (ShouldUseNetworkObjectList()) ConsiderList.reserve(GetNetworkObjectList().ActiveNetworkObjects.Num()); auto World = GetWorld(); ServerReplicateActors_BuildConsiderList(ConsiderList, ServerTickTime); // LOG_INFO(LogReplication, "Considering {} actors.", ConsiderList.size()); static UChannel* (*CreateChannel)(UNetConnection*, int, bool, int32_t) = decltype(CreateChannel)(Addresses::CreateChannel); static __int64 (*ReplicateActor)(UActorChannel*) = decltype(ReplicateActor)(Addresses::ReplicateActor); static UObject* (*CreateChannelByName)(UNetConnection * Connection, FName * ChName, EChannelCreateFlags CreateFlags, int32_t ChannelIndex) = decltype(CreateChannelByName)(Addresses::CreateChannel); static __int64 (*SetChannelActor)(UActorChannel*, AActor*) = decltype(SetChannelActor)(Addresses::SetChannelActor); for (int32 i = 0; i < this->GetClientConnections().Num(); i++) { UNetConnection* Connection = this->GetClientConnections().at(i); if (!Connection) continue; if (i >= NumClientsToTick) continue; if (!Connection->GetViewTarget()) continue; if (Addresses::SendClientAdjustment) { 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(); } */ std::vector DeletionEntries; auto ConnectionDestroyedStartupOrDormantActors = GetDestroyedStartupOrDormantActors(Connection); if (ConnectionDestroyedStartupOrDormantActors) { auto DriverDestroyedStartupOrDormantActors = GetDestroyedStartupOrDormantActors(this); if (DriverDestroyedStartupOrDormantActors) { for (FNetworkGUID& ConnectionIt : *ConnectionDestroyedStartupOrDormantActors) { FActorDestructionInfo* DInfo = nullptr; for (TPair& DriverIt : *DriverDestroyedStartupOrDormantActors) { if (DriverIt.First == ConnectionIt) { DInfo = &DriverIt.Second; break; } } if (!DInfo) continue; // should never happen DeletionEntries.push_back(DInfo); } } } LOG_INFO(LogDev, "DeletionEntries: {}", DeletionEntries.size()); for (FActorDestructionInfo* DeletionEntry : DeletionEntries) { LOG_INFO(LogDev, "AA: {}", DeletionEntry->PathName.Data.Data ? DeletionEntry->PathName.ToString() : "Null"); if (DeletionEntry->StreamingLevelName != -1) { auto ClientVisibleLevelNames = GetClientVisibleLevelNames(Connection); bool bFound = false; for (FName& ClientVisibleLevelName : *ClientVisibleLevelNames) { if (ClientVisibleLevelName == DeletionEntry->StreamingLevelName) { bFound = true; break; } } if (!bFound) continue; } UActorChannel* Channel = nullptr; if (Engine_Version >= 422) { FString ActorStr = L"Actor"; FName ActorName = UKismetStringLibrary::Conv_StringToName(ActorStr); int ChannelIndex = -1; // 4294967295 Channel = (UActorChannel*)CreateChannelByName(Connection, &ActorName, EChannelCreateFlags::OpenedLocally, ChannelIndex); } else { Channel = (UActorChannel*)CreateChannel(Connection, 2, true, -1); } if (Channel) { // FinalRelevantCount++; SetChannelActorForDestroy(Channel, DeletionEntry); // Send a close bunch on the new channel GetDestroyedStartupOrDormantActors(Connection)->Remove(DeletionEntry->NetGUID); // Remove from connections to-be-destroyed list (close bunch of reliable, so it will make it there) } } for (auto& ActorInfo : ConsiderList) { if (!ActorInfo || !ActorInfo->Actor) continue; auto Actor = ActorInfo->Actor; auto Channel = FindChannel(Actor, Connection); /* if (IsActorDormant(ActorInfo, WeakConnection)) { continue; } */ std::vector ConnectionViewers; ConnectionViewers.push_back(ConstructNetViewer(Connection)); const bool bLevelInitializedForActor = IsLevelInitializedForActor(Actor, Connection); if (!Channel) { // if (!IsLevelInitializedForActor(Actor, Connection)) if (!bLevelInitializedForActor) { // If the level this actor belongs to isn't loaded on client, don't bother sending continue; } /* if (!IsActorRelevantToConnection(Actor, ConnectionViewers)) { // 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()) { if (Connection && Connection->GetViewTarget()) { auto Viewer = Connection->GetViewTarget(); auto Loc = Viewer->GetActorLocation(); if (!IsActorRelevantToConnection(Actor, ConnectionViewers)) { // LOG_INFO(LogReplication, "Actor is not relevant!"); if (Channel) ActorChannelClose(Channel); continue; } } } } if (!Channel) { if (Actor->IsA(APlayerController::StaticClass()) && Actor != Connection->GetPlayerController()) // isnetrelevantfor should handle this iirc continue; if (bLevelInitializedForActor) { if (Engine_Version >= 422) { FString ActorStr = L"Actor"; FName ActorName = UKismetStringLibrary::Conv_StringToName(ActorStr); int ChannelIndex = -1; // 4294967295 Channel = (UActorChannel*)CreateChannelByName(Connection, &ActorName, EChannelCreateFlags::OpenedLocally, ChannelIndex); } else { Channel = (UActorChannel*)CreateChannel(Connection, 2, true, -1); } if (Channel) { SetChannelActor(Channel, Actor); } } else if (Actor->GetNetUpdateFrequency() < 1.0f) { ActorInfo->NextUpdateTime = UGameplayStatics::GetTimeSeconds(GetWorld()) + 0.2f * FRand(); } } if (Channel) { if (ReplicateActor(Channel)) { if (ShouldUseNetworkObjectList()) { // LOG_INFO(LogReplication, "Replicated Actor!"); auto TimeSeconds = UGameplayStatics::GetTimeSeconds(World); const float MinOptimalDelta = 1.0f / Actor->GetNetUpdateFrequency(); const float MaxOptimalDelta = max(1.0f / Actor->GetMinNetUpdateFrequency(), MinOptimalDelta); const float DeltaBetweenReplications = (TimeSeconds - ActorInfo->LastNetReplicateTime); // Choose an optimal time, we choose 70% of the actual rate to allow frequency to go up if needed ActorInfo->OptimalNetUpdateDelta = std::clamp(DeltaBetweenReplications * 0.7f, MinOptimalDelta, MaxOptimalDelta); // should we use fmath? ActorInfo->LastNetReplicateTime = TimeSeconds; } } } } } // shuffle the list of connections if not all connections were ticked /* if (NumClientsToTick < NetDriver->ClientConnections.Num()) { int32 NumConnectionsToMove = NumClientsToTick; while (NumConnectionsToMove > 0) { // move all the ticked connections to the end of the list so that the other connections are considered first for the next frame UNetConnection* Connection = NetDriver->ClientConnections[0]; NetDriver->ClientConnections.RemoveAt(0, 1); NetDriver->ClientConnections.Add(Connection); NumConnectionsToMove--; } } */ return Updated; }