Skip to content

Commit

Permalink
Implemented first draft of multiplayer functional tests + moved test …
Browse files Browse the repository at this point in the history
…assets out of editor folder
  • Loading branch information
JonasReich committed Feb 26, 2024
1 parent 48dc938 commit 86780fc
Show file tree
Hide file tree
Showing 28 changed files with 641 additions and 2 deletions.
Binary file modified Content/Editor/Tests/BP_BpInterface_BpImpl.uasset
Binary file not shown.
Binary file modified Content/Editor/Tests/Blueprint/FT_Sorting.uasset
Binary file not shown.
Binary file modified Content/Editor/Tests/Blueprint/FTest_Blueprint.umap
Binary file not shown.
Binary file modified Content/Editor/Tests/Core/DT_DataTableFunctionsTestTable.uasset
Binary file not shown.
Binary file modified Content/Editor/Tests/Core/FT_DataTableFunctions.uasset
Binary file not shown.
Binary file modified Content/Editor/Tests/Core/FT_GetClassDefaultObject.uasset
Binary file not shown.
Binary file modified Content/Editor/Tests/Core/FT_GetWorld.uasset
Binary file not shown.
Binary file modified Content/Editor/Tests/Core/FTest_Core.umap
Binary file not shown.
Binary file added Content/Runtime/BPML_Actor.uasset
Binary file not shown.
Binary file added Content/Tests/BP_BpInterface_BpImpl.uasset
Binary file not shown.
Binary file added Content/Tests/Blueprint/FT_Sorting.uasset
Binary file not shown.
Binary file added Content/Tests/Blueprint/FTest_Blueprint.umap
Binary file not shown.
Binary file not shown.
Binary file added Content/Tests/Core/FT_DataTableFunctions.uasset
Binary file not shown.
Binary file not shown.
Binary file added Content/Tests/Core/FT_GetWorld.uasset
Binary file not shown.
Binary file added Content/Tests/Core/FTest_Core.umap
Binary file not shown.
Binary file not shown.
Binary file added Content/Tests/Multiplayer/FT_Multiplayer.uasset
Binary file not shown.
Binary file added Content/Tests/Multiplayer/FTest_Multiplayer.umap
Binary file not shown.
8 changes: 8 additions & 0 deletions OpenUnrealUtilities.uplugin
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,21 @@
"Name": "EditorScriptingUtilities",
"Enabled": true
},
{
"Name": "Gauntlet",
"Enabled": true
},
{
"Name": "GameplayAbilities",
"Enabled": true
},
{
"Name": "DataValidation",
"Enabled": true
},
{
"Name": "OnlineSubsystemUtils",
"Enabled": true
}
]
}
7 changes: 5 additions & 2 deletions Source/OUUTestUtilities/OUUTestUtilities.Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ public OUUTestUtilities(ReadOnlyTargetRules Target) : base(Target)
"CoreUObject",
"Engine",
"UMG",
"SignificanceManager",
"AutomationController",
"Gauntlet",
"EngineSettings",
"OnlineSubsystem",
"OnlineSubsystemUtils",
"FunctionalTesting",

// Plugin
"OUURuntime"
"OUURuntime",
});

if (Target.bBuildEditor)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright (c) 2024 Jonas Reich & Contributors

#include "Multiplayer/OUUMultiplayerFunctionalTest.h"

#include "Animation/BoneChainRange.h"
#include "Multiplayer/OUUMultiplayerTestClientSignal.h"
#include "Multiplayer/OUUMultiplayerTestController.h"
#include "Net/UnrealNetwork.h"

AOUUMultiplayerFunctionalTest::AOUUMultiplayerFunctionalTest()
{
bReplicates = true;
}

int32 AOUUMultiplayerFunctionalTest::AdvanceLocalSyncMarker()
{
LastLocalSyncMarker++;
if (HasAuthority())
{
// DO NOT acknowledge yet!
// wait for client markers first...
// LastServerAcknowledgedSyncMarker = LastLocalSyncMarker;
UOUUMultiplayerTestController::Get().MarkHeartbeatActive(
FString::Printf(TEXT("[SERVER] Sync Marker %i"), LastLocalSyncMarker));
}
else
{
int32 NumSignalActors = 0;
for (auto Signal : TActorRange<AOUUMultiplayerTestClientSignal>(GetWorld()))
{
NumSignalActors++;
// find the signal actor we can use to send RPCs to the server
if (Signal->GetOwner())
{
Signal->Server_NotifySyncPointReached(this, LastLocalSyncMarker);
return LastLocalSyncMarker;
}
}
checkf(false, TEXT("None of the %i client signals have automous proxy role for this client"), NumSignalActors);
}
return LastLocalSyncMarker;
}

void AOUUMultiplayerFunctionalTest::ServerNotifyClientSyncMarkerReached(
APlayerController* ClientPlayer,
int32 SyncMarker)
{
UOUUMultiplayerTestController::Get().MarkHeartbeatActive(
FString::Printf(TEXT("[SERVER] Client Sync Marker %i"), SyncMarker));
auto& UpdateEntry = ClientSyncMarkerLocations.FindOrAdd(ClientPlayer, -1);

// check expected sync marker state. If this check fails, some sync marker was skipped.
ensure(UpdateEntry == -1 || UpdateEntry == SyncMarker - 1 || UpdateEntry == SyncMarker);
UpdateEntry = SyncMarker;

// #TODO make parameter
if (ClientSyncMarkerLocations.Num() < 2)
{
// not enough clients.
// only relevant for first sync point until the map is filled with all players.
return;
}

int32 LastClientAcknowledgedMarker = -1;
for (auto& Entry : ClientSyncMarkerLocations)
{
if (LastClientAcknowledgedMarker == -1)
{
LastClientAcknowledgedMarker = Entry.Value;
}
else
{
LastClientAcknowledgedMarker = FMath::Min(Entry.Value, LastClientAcknowledgedMarker);
}
UE_LOG(LogTemp, Log, TEXT("%i"), Entry.Value);
}
UE_LOG(LogTemp, Log, TEXT("Last ACK Marker %i"), LastClientAcknowledgedMarker);

if (LastClientAcknowledgedMarker == LastLocalSyncMarker)
{
LastServerAcknowledgedSyncMarker = LastLocalSyncMarker;
UOUUMultiplayerTestController::Get().MarkHeartbeatActive(
FString::Printf(TEXT("[SERVER] ACK Sync Marker %i"), LastServerAcknowledgedSyncMarker));

OnSyncMarkerReached.Broadcast(LastClientAcknowledgedMarker);
}
}
bool AOUUMultiplayerFunctionalTest::RunTest(const TArray<FString>& Params)
{
UOUUMultiplayerTestController::Get().NotifyFunctionalTestStarted();
return Super::RunTest(Params);
}

void AOUUMultiplayerFunctionalTest::FinishTest(EFunctionalTestResult TestResult, const FString& Message)
{
ensureMsgf(
HasAuthority(),
TEXT("The FinishTest function should only ever be called on Authority! Use snyc point nodes for all "
"intermediate steps you want to ensure synchronicity."));
UOUUMultiplayerTestController::Get().NotifyFunctionalTestEnded(TestResult);
Super::FinishTest(TestResult, Message);
}

void AOUUMultiplayerFunctionalTest::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AOUUMultiplayerFunctionalTest, LastServerAcknowledgedSyncMarker);
}

void AOUUMultiplayerFunctionalTest::OnRep_ServerSyncMarker() const
{
ensure(LastServerAcknowledgedSyncMarker == LastLocalSyncMarker);
if (OnSyncMarkerReached.IsBound())
{
OnSyncMarkerReached.Broadcast(LastServerAcknowledgedSyncMarker);
}
}

UOUUMultiplayerTestWaitForAll* UOUUMultiplayerTestWaitForAll::WaitForAll(AOUUMultiplayerFunctionalTest* InOwningTest)
{
UOUUMultiplayerTestWaitForAll* Proxy = NewObject<UOUUMultiplayerTestWaitForAll>();
Proxy->OwningTest = InOwningTest;
return Proxy;
}

void UOUUMultiplayerTestWaitForAll::Activate()
{
OwningTest->OnSyncMarkerReached.AddUObject(this, &UOUUMultiplayerTestWaitForAll::HandleSyncMarkerReached);
MarkerIdx = OwningTest->AdvanceLocalSyncMarker();
}

void UOUUMultiplayerTestWaitForAll::HandleSyncMarkerReached(int32 Marker) const
{
ensure(MarkerIdx == Marker);
OwningTest->OnSyncMarkerReached.RemoveAll(this);
OnComplete.Broadcast();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2024 Jonas Reich & Contributors

#include "Multiplayer/OUUMultiplayerTestClientSignal.h"

#include "GameFramework/PlayerController.h"
#include "Multiplayer/OUUMultiplayerFunctionalTest.h"
#include "Multiplayer/OUUMultiplayerTestController.h"

AOUUMultiplayerTestClientSignal::AOUUMultiplayerTestClientSignal()
{
bReplicates = true;
bAlwaysRelevant = true;
NetUpdateFrequency = 1.f;
}

void AOUUMultiplayerTestClientSignal::BeginPlay()
{
Super::BeginPlay();
if (HasAuthority())
{
// ???
}
else if (GetOwner())
{
Server_NotifySignalOnClientSpawned();
}
}

void AOUUMultiplayerTestClientSignal::Server_NotifySignalOnClientSpawned_Implementation()
{
UOUUMultiplayerTestController::Get().NotifyServerPostSignalReplicated();
}

void AOUUMultiplayerTestClientSignal::Server_NotifySyncPointReached_Implementation(
AOUUMultiplayerFunctionalTest* Test,
int32 SyncPoint)
{
if (!ensure(Test))
return;

auto* OwningPlayer = Cast<APlayerController> (GetOwner());
check(OwningPlayer);
Test->ServerNotifyClientSyncMarkerReached(OwningPlayer, SyncPoint);
}
Loading

0 comments on commit 86780fc

Please sign in to comment.