x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Clients spectating instead of playing (replication issues?)

I made some slight changes to my game, that should in no way influence the spectating state: I created a new c++ class based on HUD, which just adds a Widget to the PlayerScreen on BeginPlay. I selected that HUD class as my default HUD class in my BP_Gamemode and I modified my BirdsEyePawn, so it does not create a Widget anymore.

After I made the changes, when I play in editor with a dedicated server, the client does not spawn the default pawn anymore and spawns the SpectatorPawn instead. When playing with a listen server and a client, the listen server still spawns the default pawn.

These are the relevant logs, when I get the SpecatorPawn instead of my DefaultPawn (BP_BirdsEyePawn):

 LogGameMode:Display: Match State Changed from WaitingToStart to InProgress
 LogBaseGameMode:Verbose: Spawned Default Pawn 'BP_BirdsEyePawn_C_0' for 'BP_BasePlayerController_C_0'
 LogGameState: Match State Changed from WaitingToStart to InProgress
 LogBasePlayerController:Verbose: NULL GameState when trying to spawn spectator!
 LogSpawn:Warning: SpawnActor failed because no class was specified
 LogBasePlayerController:Verbose: Spawned spectator SpectatorPawn_0 [server:0]

The SpawnActor failure seems to be the PlayerCameraManager, because I do not have a PlayerCameraManager Class assigned in my BP_PlayerController, and that Log dissappears, when I assign a PlayerCameraManager class. Weirdly, when I go back to the previous version in Git, the PlayerCameraManager Class is still unassigned, but the SpawnActor failure does not seem to appear.

The other message missing from the logs, when I play with the previous version is that the SpectatorPawn_0 was spawned. So maybe the match state is not correctly replicated to the client?

I am really at a loss about what is going on. Any help or things I could try would be very much appreciated.

If someone from Epic chimes in, I would happily send you a download link to my project. I would prefer to send the download link to an email address or through some other medium, where I don't have to share the link with the whole internet.

Product Version: UE 4.13
Tags:
more ▼

asked Oct 28 '16 at 09:25 AM in C++ Programming

avatar image

AllJonasNeeds
249 26 36 71

avatar image AllJonasNeeds Oct 28 '16 at 10:03 AM

I found, that when SpawnSpectatorPawn() is called, that the MatchState is InProgress, which I don't think should happen. This is the relevant part of the call stack:

 LogOutputDevice:Error: === Handled ensure: ===
 LogOutputDevice:Error: Ensure condition failed: GetWorld()->GameState->GetMatchState() != MatchState::InProgress [File:C:\Users\alljo\Documents\Unreal Projects\gameCPP\RobotDefenseCPP\Source\RobotDefenseCPP\Player\BasePlayerController.cpp] [Line: 60]
 LogOutputDevice:Error: Stack: 
 LogOutputDevice:Error: UE4Editor-Core.dll!FWindowsPlatformStackWalk::StackWalkAndDump() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\core\private\windows\windowsplatformstackwalk.cpp:183]
 LogOutputDevice:Error: UE4Editor-Core.dll!FDebug::EnsureFailed() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\core\private\misc\outputdevice.cpp:297]
 LogOutputDevice:Error: UE4Editor-Core.dll!FDebug::OptionallyLogFormattedEnsureMessageReturningFalse() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\core\private\misc\outputdevice.cpp:432]
 LogOutputDevice:Error: UE4Editor-RobotDefenseCPP-3129.dll
 LogOutputDevice:Error: UE4Editor-Engine.dll!APlayerController::BeginSpectatingState() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\engine\private\playercontroller.cpp:4085]
 LogOutputDevice:Error: UE4Editor-Engine.dll!APlayerController::ReceivedSpectatorClass() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\engine\private\playercontroller.cpp:3726]
 LogOutputDevice:Error: UE4Editor-Engine.dll!AGameState::ReceivedSpectatorClass() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\engine\private\gamestate.cpp:112]
 LogOutputDevice:Error: UE4Editor-CoreUObject.dll!UFunction::Invoke() [d:\build\++ue4+release-4.13+compile\sync\engine\source\runtime\coreuobject\private\uobject\class.cpp:4280]
avatar image AllJonasNeeds Oct 28 '16 at 10:06 AM

And this is the code, where I overrode SpawnSpectatorPawn() in my BasePlayerController.cpp:

 ASpectatorPawn * ABasePlayerController::SpawnSpectatorPawn()
 {
     if (GetWorld()->GameState)
     {
         UE_LOG(LogBasePlayerController, Verbose, TEXT("MatchState: %s"), *GetWorld()->GameState->GetMatchState().ToString());
         ensure(GetWorld()->GameState->GetMatchState() != MatchState::InProgress);
     }
     ...
avatar image AllJonasNeeds Oct 28 '16 at 10:17 AM

So it seems, like some things are happening, when the following function is called:

 void APlayerController::ReceivedSpectatorClass(TSubclassOf<ASpectatorPawn> SpectatorClass)
 {
     if (IsInState(NAME_Spectating))
     {
         if (GetSpectatorPawn() == NULL)
         {
             BeginSpectatingState();
         }
     }
 }

IsInState(NAME_Spectating) returns true, even though GameState->MatchState is 'InProgress'. So the GameState does not pass the MatchState down to the client's version of its PlayerController, because IsInState() just checks the local StateName variable.

avatar image AllJonasNeeds Oct 28 '16 at 10:25 AM

Edit: Nevermind, what I wrote before, I just had a different SpawnActor() failure, because my default HUD class in my BP_Gamemode was 'None'.

avatar image AllJonasNeeds Oct 28 '16 at 11:07 AM

I investigated, when the change is changed. The Player Controller's State Name is changed in 2 places: In APlayerController::PostInitializeComponents() the StateName is set to NAME_Spectating. And in APlayerController::ChangeState is changed normally. I set up breakpoints in Visual Studio and it looks like the PlayerController is created, setting the StateName to NAME_Spectating. Then ChangeState is called, changing it to the Playing state, but then the PlayerController is constructed again, which changes the StateName to NAME_Spectating, but it is never changed back to the Playing state.

avatar image AllJonasNeeds Oct 28 '16 at 12:30 PM

for some reason, when APlayerController::ClientRestart_Implementation(APawn* NewPawn) is called, NewPawn is NULL, resulting in the Client's Pawn being set to NULL, and the PlayerController not changing to Playing state.

(comments are locked)
10|2000 characters needed characters left

1 answer: sort voted first

I was able to find out what caused the change in behaviour. By adding the following functions back into my BirdsEyePawn, I was able to recreate the previous behaviour:

 BirdsEyePawn.h
 public:
     virtual void UnPossessed() override;
     virtual void PossessedBy(AController* NewController) override;
 protected:
     UFUNCTION(client, reliable)
         virtual void Client_UnPossessed();
     UFUNCTION(client, reliable)
         virtual void Client_PossessedBy(AController* NewController);
 
 BirdsEyePawn.cpp
 void ABirdsEyePawn::UnPossessed()
 {
     Super::UnPossessed();
     Client_UnPossessed();
 }
 
 void ABirdsEyePawn::Client_UnPossessed_Implementation()
 {
 }
 
 void ABirdsEyePawn::PossessedBy(AController * NewController)
 {
     Super::PossessedBy(NewController);
     Client_PossessedBy(NewController);
 }
 
 void ABirdsEyePawn::Client_PossessedBy_Implementation(AController* NewController)
 {
 }

That made me realize, what might actually be happening: My replicated function call inside ABirdsEyePawn::PossessedBy is delaying the APlayerController::ClientRestart() call just long enough, that the Client already has destroyed the placeholder PlayerController, which then receives the ClientRestart() call, resulting in the Client's PlayerController performing a false Restart and acting as if it possessed my BirdsEyePawn for a couple of seconds, until the BirdsEyePawn is garbage collected.

So my actual problem is something else and I will probably create a new answerhub post, once I figured that out, because no one wants to read through all my crap.

Or at least I wouldn't want to read through all of that :)

EDIT: Nevermind, I found the solution. A while ago I overrode APawn::IsNetRelevantFor() like so:

 bool ABirdsEyePawn::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
 {
     // BirdsEyePawn is never relevant to the client controlling it, because the controller acts authoritative
     if (RealViewer == Controller || this == ViewTarget)
     {
         return false;
     }
     else return Super::IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
 }

That of course resulted in the Pawn being deleted on the owning client, which in turn resulted in the above dilemma.

more ▼

answered Oct 28 '16 at 01:24 PM

avatar image

AllJonasNeeds
249 26 36 71

(comments are locked)
10|2000 characters needed characters left
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question