Skip to content

Commit

Permalink
Added more comments + cleaned up strucutre of MP tests
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasReich committed Mar 3, 2024
1 parent 4a6e47b commit ac87f48
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@ int32 AOUUMultiplayerFunctionalTest::AdvanceLocalSyncMarker()
LastLocalSyncMarker++;
if (HasAuthority())
{
// DO NOT acknowledge yet!
// DO NOT acknowledge yet! (LastServerAcknowledgedSyncMarker = LastLocalSyncMarker)
// 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()))
for (auto* Signal : TActorRange<AOUUMultiplayerTestClientSignal>(GetWorld()))
{
NumSignalActors++;
// find the signal actor we can use to send RPCs to the server
Expand Down Expand Up @@ -62,7 +61,7 @@ void AOUUMultiplayerFunctionalTest::ServerNotifyClientSyncMarkerReached(
}

int32 LastClientAcknowledgedMarker = -1;
for (auto& Entry : ClientSyncMarkerLocations)
for (const auto& Entry : ClientSyncMarkerLocations)
{
if (LastClientAcknowledgedMarker == -1)
{
Expand Down Expand Up @@ -106,12 +105,6 @@ void AOUUMultiplayerFunctionalTest::FinishTest(EFunctionalTestResult TestResult,
Multicast_OnTestEnded(TestResult, TestIndex, TotalNumTests);
}

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

void AOUUMultiplayerFunctionalTest::Multicast_OnTestStarted_Implementation()
{
UOUUMultiplayerTestController::Get().NotifyFunctionalTestStarted();
Expand All @@ -132,22 +125,8 @@ void AOUUMultiplayerFunctionalTest::OnRep_ServerSyncMarker() const
}
}

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
void AOUUMultiplayerFunctionalTest::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
ensure(MarkerIdx == Marker);
OwningTest->OnSyncMarkerReached.RemoveAll(this);
OnComplete.Broadcast();
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AOUUMultiplayerFunctionalTest, LastServerAcknowledgedSyncMarker);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2024 Jonas Reich & Contributors

#include "Multiplayer/OUUMultiplayerTestWaitForAll.h"

#include "Multiplayer/OUUMultiplayerFunctionalTest.h"

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
Expand Up @@ -5,10 +5,11 @@
#include "CoreMinimal.h"

#include "FunctionalTest.h"
#include "Kismet/BlueprintAsyncActionBase.h"

#include "OUUMultiplayerFunctionalTest.generated.h"

// Base class for multiplayer functional tests executed via Gauntlet.
// These tests do not work in editor since they require the gauntlet controller for coordination between agents.
UCLASS(Blueprintable)
class OUUTESTUTILITIES_API AOUUMultiplayerFunctionalTest : public AFunctionalTest
{
Expand All @@ -18,71 +19,45 @@ class OUUTESTUTILITIES_API AOUUMultiplayerFunctionalTest : public AFunctionalTes

AOUUMultiplayerFunctionalTest();

// For tracking how far we are along the test progression
// For tracking where this test lies in the progression of all tests executed by the current gauntlet controller
int32 TestIndex = INDEX_NONE;
int32 TotalNumTests = INDEX_NONE;

// Called on server + all clients if we detected that a sync point was reached.
// Called on server + all clients when a sync point was reached.
DECLARE_EVENT_OneParam(AOUUMultiplayerFunctionalTest, FOnSyncMarkerReached, int32);
FOnSyncMarkerReached OnSyncMarkerReached;

// Called on server + all clients after the test was started.
// Use this as a start trigger in Blueprint graphs instead of the regular functional test start event.
UFUNCTION(BlueprintImplementableEvent, DisplayName = "Multiplayer Test Started")
void K2_OnTestStarted();

int32 AdvanceLocalSyncMarker();
void ServerNotifyClientSyncMarkerReached(APlayerController* ClientPlayer, int32 SyncMarker);

// - AFunctionalTest
bool RunTest(const TArray<FString>& Params) override;
void FinishTest(EFunctionalTestResult TestResult, const FString& Message) override;

// - UObject
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
// --

public:
UFUNCTION(NetMulticast, Reliable)
void Multicast_OnTestStarted();
UFUNCTION(BlueprintImplementableEvent, DisplayName = "Multiplayer Test Started")
void K2_OnTestStarted();
UFUNCTION(NetMulticast, Reliable)
void Multicast_OnTestEnded(EFunctionalTestResult TestResult, int32 InTestIndex, int32 InTotalNumTests);

private:
UPROPERTY(Transient)
TMap<APlayerController*, int32> ClientSyncMarkerLocations;

UPROPERTY(Transient, Replicated, ReplicatedUsing=OnRep_ServerSyncMarker)
UPROPERTY(Transient, Replicated, ReplicatedUsing = OnRep_ServerSyncMarker)
int32 LastServerAcknowledgedSyncMarker = -1;

int32 LastLocalSyncMarker = -1;


UFUNCTION(NetMulticast, Reliable)
void Multicast_OnTestStarted();

UFUNCTION(NetMulticast, Reliable)
void Multicast_OnTestEnded(EFunctionalTestResult TestResult, int32 InTestIndex, int32 InTotalNumTests);

UFUNCTION()
void OnRep_ServerSyncMarker() const;
};

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnOUUMultiplayerWaitForAll);

// This is the actual signal used in Blueprints of the functional tests
UCLASS(MinimalAPI)
class UOUUMultiplayerTestWaitForAll : public UBlueprintAsyncActionBase
{
GENERATED_BODY()

public:
UPROPERTY(BlueprintAssignable)
FOnOUUMultiplayerWaitForAll OnComplete;

UFUNCTION(
BlueprintCallable,
meta = (BlueprintInternalUseOnly = "true", DefaultToSelf = "InOwningTest"),
Category = "OpenUnrealUtilities|Testing")
static UOUUMultiplayerTestWaitForAll* WaitForAll(AOUUMultiplayerFunctionalTest* InOwningTest);

// - UBlueprintAsyncActionBase
void Activate() override;
// --
private:
UPROPERTY()
AOUUMultiplayerFunctionalTest* OwningTest = nullptr;
int32 MarkerIdx = -1;

void HandleSyncMarkerReached(int32 Marker) const;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2024 Jonas Reich & Contributors

#pragma once

#include "CoreMinimal.h"

#include "Kismet/BlueprintAsyncActionBase.h"

#include "OUUMultiplayerTestWaitForAll.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnOUUMultiplayerWaitForAll);

// Helper node to wait for on all sides (host & clients) for everyone to reach the same execution point.
// Only usable in the multiplayer functional tests.
// Triggering the node on either side triggers a mechanism wherein all parties agree which "sync marker" they last
// reached. OnComplete is called after server + clients completed this handshake (server initiated).
UCLASS(MinimalAPI)
class UOUUMultiplayerTestWaitForAll : public UBlueprintAsyncActionBase
{
GENERATED_BODY()

public:
UPROPERTY(BlueprintAssignable)
FOnOUUMultiplayerWaitForAll OnComplete;

UFUNCTION(
BlueprintCallable,
meta = (BlueprintInternalUseOnly = "true", DefaultToSelf = "InOwningTest"),
Category = "OpenUnrealUtilities|Testing")
static UOUUMultiplayerTestWaitForAll* WaitForAll(AOUUMultiplayerFunctionalTest* InOwningTest);

// - UBlueprintAsyncActionBase
void Activate() override;
// --
private:
UPROPERTY()
AOUUMultiplayerFunctionalTest* OwningTest = nullptr;
int32 MarkerIdx = -1;

void HandleSyncMarkerReached(int32 Marker) const;
};

0 comments on commit ac87f48

Please sign in to comment.